diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..600e365 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/node_modules \ No newline at end of file diff --git a/.github/workflows/gradle-actions.yml b/.github/workflows/gradle-actions.yml new file mode 100644 index 0000000..2774a63 --- /dev/null +++ b/.github/workflows/gradle-actions.yml @@ -0,0 +1,51 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle + +name: build image + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: docker/login-action@v3.1.0 + name: Docker login + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - uses: docker/setup-buildx-action@v3.3.0 + with: + platforms: linux/amd64 + + - uses: actions/checkout@v4 + - name: Server Build and push Docker images + uses: docker/build-push-action@v5.3.0 + with: + file: Dockerfile-system + platforms: linux/amd64 + tags: zerolinck/passiflora-system:latest + push: true + + - name: UI Build and push Docker images + uses: docker/build-push-action@v5.3.0 + with: + file: Dockerfile-ui + platforms: linux/amd64 + tags: zerolinck/passiflora-ui:latest + push: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3846317 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +### 日志 ### +**/logs +**/.gradle +**/build +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +**/.idea +**/.iws +/**/*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + + +**/node_modules +**/dist diff --git a/Dockerfile-builder b/Dockerfile-builder new file mode 100644 index 0000000..f0c0954 --- /dev/null +++ b/Dockerfile-builder @@ -0,0 +1,5 @@ +FROM openjdk:21-slim +COPY passiflora-server /app/ +WORKDIR /app/ +RUN ["./gradlew", "dependencies", "--refresh-dependencies"] +RUN rm -rf /app \ No newline at end of file diff --git a/Dockerfile-gateway b/Dockerfile-gateway new file mode 100644 index 0000000..b26d96d --- /dev/null +++ b/Dockerfile-gateway @@ -0,0 +1,12 @@ +FROM zerolinck/passiflora-server-builder AS build +COPY passiflora-server /app +WORKDIR /app +RUN ./gradlew :passiflora-gateway-app:build -x test -Denv=prod + +FROM openjdk:21-jdk +ARG JAR_FILE=passiflora-gateway-*.jar +COPY --from=build /app/passiflora-gateway-app/build/libs/${JAR_FILE} /app/app.jar +WORKDIR /app +VOLUME /etc/localtime:/etc/localtime:ro +EXPOSE 51000 +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/Dockerfile-storage b/Dockerfile-storage new file mode 100644 index 0000000..e10c737 --- /dev/null +++ b/Dockerfile-storage @@ -0,0 +1,12 @@ +FROM zerolinck/passiflora-server-builder AS build +COPY passiflora-server /app +WORKDIR /app +RUN ./gradlew :passiflora-storage-app:build -x test -Denv=prod + +FROM openjdk:21-jdk +ARG JAR_FILE=passiflora-storage-*.jar +COPY --from=build /app/passiflora-storage-app/build/libs/${JAR_FILE} /app/app.jar +WORKDIR /app +VOLUME /etc/localtime:/etc/localtime:ro +EXPOSE 51002 +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/Dockerfile-system b/Dockerfile-system new file mode 100644 index 0000000..0b98cf9 --- /dev/null +++ b/Dockerfile-system @@ -0,0 +1,12 @@ +FROM zerolinck/passiflora-server-builder AS build +COPY passiflora-server /app +WORKDIR /app +RUN ./gradlew :passiflora-system-app:build -x test -Denv=prod + +FROM openjdk:21-jdk +ARG JAR_FILE=passiflora-system-*.jar +COPY --from=build /app/passiflora-system-app/build/libs/${JAR_FILE} /app/app.jar +WORKDIR /app +VOLUME /etc/localtime:/etc/localtime:ro +EXPOSE 51001 +ENTRYPOINT java $JAVA_OPTS -jar app.jar \ No newline at end of file diff --git a/Dockerfile-ui b/Dockerfile-ui new file mode 100644 index 0000000..8be0d67 --- /dev/null +++ b/Dockerfile-ui @@ -0,0 +1,10 @@ +FROM node:20 AS build +COPY passiflora-ui /app +WORKDIR /app +RUN npm install +RUN npm run build + +FROM nginx:1.21 +COPY passiflora-ui/nginx.conf /etc/nginx/nginx.conf +COPY --from=build /app/dist /usr/share/nginx/dist +VOLUME /etc/localtime:/etc/localtime:ro diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..61a04fb --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,16 @@ +pipeline { + agent any + environment { + def change = '' + } + stages { + stage('Deploy') { + steps { + script { + change = getChanges() + } + sh 'docker-compose up -d --build' + } + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /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) + + 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/README.md b/README.md new file mode 100644 index 0000000..f1e58cc --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +
+

Passiflora

+
+ +### 项目结构 +```shell +├── .github(github actions配置, 不使用 github actions 可删除) +├── passiflora-server (后端项目) +│   ├── modules +│   │   ├── passiflora-bom (依赖版本配置) +│   │   ├── passiflora-codegen (代码生成) +│   │   ├── passiflora-common (通用组件) +│   │   ├── passiflora-feign (Feignz组件) +│   │   └── passiflora-model (Model组件) +│   ├── passiflora-gateway-app (网关) +│   └── passiflora-system-app (系统应用) +└── passiflora-ui (前端项目) +``` +### [授权说明](document%2Fzh%2FLicense.md) + +### [多环境说明](document%2Fzh%2FMultiEnv.md) + +### [Docker-file 说明](document%2Fzh%2FDockerfile.md) + +### [单元测试说明](document%2Fzh%2FUnitTest.md) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..041c43f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,80 @@ +services: + passiflora-gateway: + build: + context: . + dockerfile: Dockerfile-gateway + environment: + TZ: Asia/Shanghai + volumes: + - /etc/localtime:/etc/localtime:ro + container_name: passiflora-gateway + restart: unless-stopped + image: passiflora-gateway + extra_hosts: + - "passiflora-nacos:host-gateway" + - "passiflora-redis:host-gateway" + expose: + - 51000 + ports: + - "51000:51000" + healthcheck: + test: [ "CMD", "curl", "http://localhost:51000" ] + start_period: 90s + interval: 60s + timeout: 10s + retries: 5 + + passiflora-system: + build: + context: . + dockerfile: Dockerfile-system + environment: + TZ: Asia/Shanghai + volumes: + - /etc/localtime:/etc/localtime:ro + container_name: passiflora-system + restart: unless-stopped + image: passiflora-system + extra_hosts: + - "passiflora-nacos:host-gateway" + - "passiflora-postgres:host-gateway" + - "passiflora-redis:host-gateway" + expose: + - 51001 + depends_on: + passiflora-gateway: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:51001/passiflora/system-api" ] + start_period: 90s + interval: 60s + timeout: 10s + retries: 5 + + passiflora-storage: + build: + context: . + dockerfile: Dockerfile-storage + volumes: + - /etc/localtime:/etc/localtime:ro + environment: + TZ: Asia/Shanghai + container_name: passiflora-storage + restart: unless-stopped + image: passiflora-file + extra_hosts: + - "passiflora-nacos:host-gateway" + - "passiflora-postgres:host-gateway" + - "passiflora-redis:host-gateway" + - "passiflora-minio:host-gateway" + expose: + - 51002 + depends_on: + passiflora-gateway: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:51002/passiflora/storage-api" ] + start_period: 90s + interval: 60s + timeout: 10s + retries: 5 diff --git a/document/en/License.md b/document/en/License.md new file mode 100644 index 0000000..6c75b8e --- /dev/null +++ b/document/en/License.md @@ -0,0 +1,9 @@ +# License Description +This project is released under the AGPL v3 license and can be used for personal learning. If you need to directly use this open-source version based on this agreement for secondary development or use this project as a module for other services, you must open-source the secondary development project and the complete service that uses this project. + +If you do not wish to open-source the secondary project or the service that uses this project, you can contact me for a commercial license. Email: zerolinck@foxmail.com. + +| System Version | Open Source Version | Commercial Version | +|-----------------|---------------------|-------------------| +| License Agreement | AGPL v3 | Perpetual License | +| Usage Restrictions | Involved projects must be open-sourced | No need for open-sourcing | \ No newline at end of file diff --git a/document/zh/Dockerfile.md b/document/zh/Dockerfile.md new file mode 100644 index 0000000..3c5d3a6 --- /dev/null +++ b/document/zh/Dockerfile.md @@ -0,0 +1,31 @@ +# Docker-file 说明 + +### [Dockerfile-builder](../../Dockerfile-builder) +构建编译镜像,此镜像直接包含编译所需依赖,加快构建速度。 +(应用镜像构建使用两阶段构建方式,创建专门用于构建的容器对项目进行编译,不使用本地环境,保证构建环境的一致性) +```shell +# 使用了交叉编译不同架构平台所使用的镜像 +# zerolinck/passiflora-server-builder 为我个人的 dockerhub 仓库 +docker buildx build --push --platform linux/arm64,linux/amd64 -t zerolinck/passiflora-server-builder -f Dockerfile-builder . +``` + +### [Dockerfile-system](../../Dockerfile-system) +构建系统服务镜像 +```shell +# 构建并推送到 dockerhub 仓库 +docker buildx build --push --platform linux/arm64,linux/amd64 -t zerolinck/passiflora-system -f Dockerfile-system . + +# 本地构建 +docker build -t zerolinck/passiflora-system -f Dockerfile-system . +``` + +### [Dockerfile-gateway](../../Dockerfile-gateway) +构建网关服务镜像 +```shell +# 构建并推送到 dockerhub 仓库 +docker buildx build --push --platform linux/arm64,linux/amd64 -t zerolinck/passiflora-gateway -f Dockerfile-gateway . + +# 本地构建 +docker build -t zerolinck/passiflora-gateway -f Dockerfile-gateway . +``` +``` \ No newline at end of file diff --git a/document/zh/License.md b/document/zh/License.md new file mode 100644 index 0000000..0cdc809 --- /dev/null +++ b/document/zh/License.md @@ -0,0 +1,9 @@ +# 授权说明 +本项目基于 AGPL v3 发布,可用于个人学习,如需直接使用此项基于此协议的开源版本进行二此开发或者将此项目作为其他服务的一个模块,则必须将二次开发的项目以及使用了此项目的完整服务进行开源。 + +如果不希望二次项目或使用了此项目的服务进行开源,可联系我获取商业授权,邮箱:zerolinck@foxmail.com。 + +| 系统版本 | 开源版本 | 商业版本 | +|------|----------|------| +| 许可协议 | AGPL v3 | 永久授权 | +| 使用限制 | 涉及项目必须开源 | 无需开源 | \ No newline at end of file diff --git a/document/zh/MultiEnv.md b/document/zh/MultiEnv.md new file mode 100644 index 0000000..e94a1f8 --- /dev/null +++ b/document/zh/MultiEnv.md @@ -0,0 +1,36 @@ +# 多环境说明 + + +在项目开发中一般都会区分开发环境 dev, 生产环境 prod, +常用的使用方式是使用每个环境一个对应的 yml 文件的方式,如下所示 + +```shell +├── application.yml +├── application-dev.yml +├── application-prod.yml +``` +Passiflora 没有采用这种方式,原因是: +1. 多 yml 的方式不便于 gradle 脚本读取。 +比如数据库配置,项目启动需要用到;liquibase 的配置在 build.gradle 中也需要使用。 +2. 有些 yml 中的配置,比如系统版本,当前环境,需要在编译的过程中,由编译程序填充到 yml 中。 + +主要基于以上两点,所以 Passiflora 将所有的连接配置,写在了 [config.groovy](..%2F..%2Fpassiflora-server%2Fconfig.groovy) 文件中, +并会在编译过程中动态生成一些配置,比如当前环境,系统版本,会将其动态填充到编译结果中的 yml 中,来进行多环境的实现。 + +![MultiEnv.png](MultiEnv.png) + +### 使用对应环境编译的方法 +```shell +# 开发环境 +gradle clean build -x test +# 默认使用的 dev 环境,所以上述命令等同于 +gradle clean build -x test -Denv=dev + +# 生产环境 +gradle clean build -x test -Denv=prod +``` + +### FAQ +1. 如果项目在启动过程中,因 yml 中存在 @xxx@ 这种没有被替换的占位标志 + 1. 多出现在使用 IDE 直接运行项目的情况,重新执行对应的编译指令再运行即可 + 2. 检查 [config.groovy](..%2F..%2Fpassiflora-server%2Fconfig.groovy) 是否确实没有对应环境的对应配置 diff --git a/document/zh/MultiEnv.png b/document/zh/MultiEnv.png new file mode 100644 index 0000000..4236fcc Binary files /dev/null and b/document/zh/MultiEnv.png differ diff --git a/document/zh/UnitTest.md b/document/zh/UnitTest.md new file mode 100644 index 0000000..9fe8532 --- /dev/null +++ b/document/zh/UnitTest.md @@ -0,0 +1,18 @@ +# 单元测试说明 +Passiflora 使用 Junit 和 mockito 对 Controller 接口进行测试, +并使用 Jcoco 生成单元测试报告。 +执行 `gradle build` 会自动执行单元测试(`gradle test`)。 + +单元测试执行完成会在 build/reports/tests/test 目录下生成网页的单元测试报告, +打开此目录下 index.html 可进行查看。 + +单元测试执行完成会在 build/jacoco 目录下生成 exec 格式的单元测试覆盖率报告, +下面会介绍查看方法。 + +### 使用 IDEA 查看单元测试覆盖率 +1. 点击 IDEA顶部标签栏中的Run +2. 选择 Show Coverage Data/Manage Coverage Reports +3. 单击 + 号选择 build/jacoco 文件夹中的 exec 文件 +4. 确认后,勾选刚刚导入进来的文件,选择Show Selected即可 + +![jcoco.png](jcoco.png) \ No newline at end of file diff --git a/document/zh/jcoco.png b/document/zh/jcoco.png new file mode 100644 index 0000000..289f40c Binary files /dev/null and b/document/zh/jcoco.png differ diff --git a/passiflora-server/build.gradle b/passiflora-server/build.gradle new file mode 100644 index 0000000..ebf893d --- /dev/null +++ b/passiflora-server/build.gradle @@ -0,0 +1,76 @@ +plugins{ + id 'java' + id 'idea' + id 'io.spring.dependency-management' version '1.1.4' + id 'com.diffplug.spotless' version '6.25.0' +} + +allprojects { + group = 'com.zerolinck' + version = '0.1' + apply plugin: 'idea' + apply plugin: 'io.spring.dependency-management' + + tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" + } + + java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + repositories { + maven { url "https://maven.aliyun.com/repository/central" } + maven { url "https://maven.aliyun.com/repository/public" } + mavenCentral() + } + + dependencyManagement{ + imports{ + mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:2023.0.1.0" + mavenBom "org.springframework.cloud:spring-cloud-dependencies:2023.0.1" + mavenBom "org.springframework.boot:spring-boot-dependencies:3.3.0" + } + } +} + +subprojects { subProject -> + if (!subProject.name.startsWith('app')) { + jar { + enabled = true + } + } +} + +spotless { + java { + target '**/*.java' + importOrder() + removeUnusedImports() + cleanthat() + // https://github.com/jhipster/prettier-java + prettier(['prettier': '3.0.3', 'prettier-plugin-java': '2.3.0']).config(['parser': 'java', 'tabWidth': 4, 'charset': 'utf-8', 'end_of_line': 'lf', 'plugins': ['prettier-plugin-java']]) + formatAnnotations() + licenseHeader '/* \n' + + ' * Copyright (C) 2024 Linck. \n' + + ' * \n' + + ' * This program is free software: you can redistribute it and/or modify\n' + + ' * it under the terms of the GNU Affero General Public License as published\n' + + ' * by the Free Software Foundation, either version 3 of the License, or\n' + + ' * (at your option) any later version.\n' + + ' * \n' + + ' * This program is distributed in the hope that it will be useful,\n' + + ' * but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + + ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' + + ' * GNU Affero General Public License for more details.\n' + + ' * \n' + + ' * You should have received a copy of the GNU Affero General Public License\n' + + ' * along with this program. If not, see .\n' + + ' */' + } +} + +compileJava { + dependsOn('spotlessApply') +} \ No newline at end of file diff --git a/passiflora-server/config.groovy b/passiflora-server/config.groovy new file mode 100644 index 0000000..1378e24 --- /dev/null +++ b/passiflora-server/config.groovy @@ -0,0 +1,66 @@ +environments { + // 开发环境 + dev { + nacos { + serverAddr = 'passiflora-nacos:8848' + } + redis { + host = "passiflora-redis" + port = "6379" + password = "123456" + database = '8' + } + minio { + endpoint = 'http://passiflora-minio:9090' + accessKey = '2Uwho01oDDbxZpzEjfb4' + secretKey = 'KIF9NCJR6SmyeVRAloQhUfrofI11FfGAhnsAXAoa' + } + system_app { + database { + url = 'jdbc:postgresql://passiflora-postgres:5432/passiflora_system?reWriteBatchedInserts=true' + username = 'postgres' + password = 'postgres' + } + } + storage_app { + database { + url = 'jdbc:postgresql://passiflora-postgres:5432/passiflora_storage?reWriteBatchedInserts=true' + username = 'postgres' + password = 'postgres' + } + } + } + + + // 线上环境 + prod { + nacos { + serverAddr = 'passiflora-nacos:8848' + } + redis { + host = "passiflora-redis" + port = "6379" + password = "123456" + database = '8' + } + minio { + endpoint = 'http://passiflora-minio:9090' + accessKey = 'pTHkPFHbhmBqgjVR35Ko' + secretKey = 'aPAtdzHGFTsI2qcxM7Q0krunDI80dd7Gtgm7grCR' + } + system_app { + database { + url = 'jdbc:postgresql://passiflora-postgres:5432/passiflora_system?reWriteBatchedInserts=true' + username = 'postgres' + password = 'postgres' + } + } + storage_app { + database { + url = 'jdbc:postgresql://passiflora-postgres:5432/passiflora_storage?reWriteBatchedInserts=true' + username = 'postgres' + password = 'postgres' + } + } + } +} \ No newline at end of file diff --git a/passiflora-server/gradle.properties b/passiflora-server/gradle.properties new file mode 100644 index 0000000..af82e00 --- /dev/null +++ b/passiflora-server/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true \ No newline at end of file diff --git a/passiflora-server/gradle/wrapper/gradle-wrapper.jar b/passiflora-server/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..d64cd49 Binary files /dev/null and b/passiflora-server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/passiflora-server/gradle/wrapper/gradle-wrapper.properties b/passiflora-server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/passiflora-server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/passiflora-server/gradlew b/passiflora-server/gradlew new file mode 100755 index 0000000..1aa94a4 --- /dev/null +++ b/passiflora-server/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/passiflora-server/gradlew.bat b/passiflora-server/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/passiflora-server/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/passiflora-server/modules/passiflora-bom/build.gradle b/passiflora-server/modules/passiflora-bom/build.gradle new file mode 100644 index 0000000..dcb7a60 --- /dev/null +++ b/passiflora-server/modules/passiflora-bom/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'java-platform' +} + +dependencies { + constraints { + api 'org.liquibase:liquibase-core:4.25.1' + api 'org.liquibase:liquibase-groovy-dsl:3.0.3' + api 'info.picocli:picocli:4.7.5' + api 'org.postgresql:postgresql:42.2.27' + api 'org.projectlombok:lombok:1.18.30' + api 'cn.hutool:hutool-all:5.8.27' + api 'cn.hutool:hutool-core:5.8.27' + api 'cn.hutool:hutool-db:5.8.27' + api 'cn.hutool:hutool-extra:5.8.27' + api 'cn.hutool:hutool-crypto:5.8.27' + api 'cn.hutool:hutool-http:5.8.27' + api 'org.mapstruct:mapstruct:1.5.5.Final' + api 'org.mapstruct:mapstruct-processor:1.5.5.Final' + api 'org.projectlombok:lombok-mapstruct-binding:0.2.0' + api 'com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:4.4.0' + api 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.20' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.5' + api 'org.freemarker:freemarker:2.3.32' + api 'org.mybatis:mybatis:3.5.16' + api 'org.mybatis:mybatis-spring:3.0.3' + api 'com.baomidou:mybatis-plus-annotation:3.5.6' + api 'com.baomidou:mybatis-plus-extension:3.5.6' + api 'com.baomidou:mybatis-plus-boot-starter:3.5.6' + api 'org.mybatis:mybatis-typehandlers-jsr310:1.0.2' + api 'org.redisson:redisson-spring-boot-starter:3.27.2' + api 'org.redisson:redisson:3.27.2' + api 'io.minio:minio:8.5.10' + + } +} + diff --git a/passiflora-server/modules/passiflora-codegen/build.gradle b/passiflora-server/modules/passiflora-codegen/build.gradle new file mode 100644 index 0000000..3affcb2 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java' +} + +dependencies { + implementation project(':modules:passiflora-common') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + runtimeOnly "org.postgresql:postgresql" + implementation 'cn.hutool:hutool-db' + implementation 'cn.hutool:hutool-extra' + implementation 'org.freemarker:freemarker' + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'com.fasterxml.jackson.core:jackson-core' +} \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/GenerateCode.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/GenerateCode.java new file mode 100644 index 0000000..11935d9 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/GenerateCode.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.zerolinck.passiflora.codegen.model.Render; +import com.zerolinck.passiflora.codegen.model.Table; +import com.zerolinck.passiflora.codegen.service.RenderService; +import com.zerolinck.passiflora.codegen.service.impl.FileRenderServiceImpl; +import com.zerolinck.passiflora.codegen.service.impl.PostgresDbService; +import com.zerolinck.passiflora.common.util.TimeUtil; +import java.util.List; +import java.util.Map; +import lombok.SneakyThrows; + +/** + * @author linck + * @since 2024-02-06 + */ +public class GenerateCode { + + @SneakyThrows + public static void main(String[] args) { + List tableNames = List.of("storage_file"); + String moduleName = "storage"; + String contextPath = "/passiflora/storage-api"; + boolean overwrite = true; + render(tableNames, moduleName, contextPath, overwrite); + } + + private static void render( + List tableNames, + String moduleName, + String contextPath, + boolean overwrite + ) { + tableNames.forEach(tableName -> { + Map dict = getInfo( + tableName, + moduleName, + contextPath + ); + RenderService renderService = new FileRenderServiceImpl(); + renderService.render( + new Render( + overwrite, + "entity.java.ftl", + "/%s.java", + "/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/" + + moduleName + + "/entity", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "convert.java.ftl", + "/%sConvert.java", + "/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/" + + moduleName + + "/mapperstruct", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "api.java.ftl", + "/%sApi.java", + "/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/" + + moduleName, + dict + ) + ); + renderService.render( + new Render( + overwrite, + "controller.java.ftl", + "/%sController.java", + "/passiflora-server/passiflora-" + + moduleName + + "-app/src/main/java/com/zerolinck/passiflora/" + + moduleName + + "/controller", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "service.java.ftl", + "/%sService.java", + "/passiflora-server/passiflora-" + + moduleName + + "-app/src/main/java/com/zerolinck/passiflora/" + + moduleName + + "/service", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "mapper.java.ftl", + "/%sMapper.java", + "/passiflora-server/passiflora-" + + moduleName + + "-app/src/main/java/com/zerolinck/passiflora/" + + moduleName + + "/mapper", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "mapper.xml.ftl", + "/%sMapper.xml", + "/passiflora-server/passiflora-" + + moduleName + + "-app/src/main/java/com/zerolinck/passiflora/" + + moduleName + + "/mapper/xml", + dict + ) + ); + renderService.render( + new Render( + overwrite, + "test.java.ftl", + "/%sControllerTest.java", + "/passiflora-server/passiflora-" + + moduleName + + "-app/src/test/java/com/zerolinck/passiflora/" + + moduleName, + dict + ) + ); + }); + } + + private static Map getInfo( + String tableName, + String moduleName, + String contextPath + ) { + Table table = new PostgresDbService().getTableInfo(tableName); + return MapUtil + .builder() + .put("contextPath", contextPath) + .put("table", table) + .put("tableName", tableName) + .put("moduleName", moduleName) + .put("date", TimeUtil.getDateStrNow()) + .put("entityName", StrUtil.toCamelCase(table.getTableName())) + .put( + "serviceName", + StrUtil.toCamelCase(table.getTableName() + "_" + "service") + ) + .put( + "mapperName", + StrUtil.toCamelCase(table.getTableName() + "_" + "mapper") + ) + .put( + "controllerName", + StrUtil.toCamelCase(table.getTableName() + "_" + "controller") + ) + .put( + "apiName", + StrUtil.toCamelCase(table.getTableName() + "_" + "api") + ) + .put( + "convertName", + StrUtil.toCamelCase(table.getTableName() + "_" + "convert") + ) + .put( + "entityClass", + StrUtil.upperFirst(StrUtil.toCamelCase(table.getTableName())) + ) + .put( + "serviceClass", + StrUtil.upperFirst( + StrUtil.toCamelCase(table.getTableName() + "_" + "service") + ) + ) + .put( + "mapperClass", + StrUtil.upperFirst( + StrUtil.toCamelCase(table.getTableName() + "_" + "mapper") + ) + ) + .put( + "controllerClass", + StrUtil.upperFirst( + StrUtil.toCamelCase( + table.getTableName() + "_" + "controller" + ) + ) + ) + .put( + "apiClass", + StrUtil.upperFirst( + StrUtil.toCamelCase(table.getTableName() + "_" + "api") + ) + ) + .put( + "convertClass", + StrUtil.upperFirst( + StrUtil.toCamelCase(table.getTableName() + "_" + "convert") + ) + ) + .build(); + } +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Column.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Column.java new file mode 100644 index 0000000..9ca0411 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Column.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.model; + +import lombok.Data; + +/** + * @author linck + * @since 2024-02-06 + */ +@Data +public class Column { + + private String columnName; + + private String fieldName; + + private String columnType; + + private Class fieldType; + + private Boolean nullable; + + private Boolean pk; + + private Integer length; + + private String description; + + private String columnDefault; +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Render.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Render.java new file mode 100644 index 0000000..600e392 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Render.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.model; + +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author linck + * @since 2024-02-07 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Render { + + private boolean override; + + private String template; + + private String fileName; + + private String path; + + private Map data; +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Table.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Table.java new file mode 100644 index 0000000..903b591 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/model/Table.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.model; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author linck + * @since 2024-02-06 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Table { + + private String tableName; + + private String pkFieldName; + + private String pkColumnName; + + private String description; + + /** 原始字段集合 */ + private List originColumnList; + + /** 移除了{@link com.zerolinck.passiflora.model.common.BaseEntity} 中已存在字段的字段集合 */ + private List columnList; + + private boolean extentBase; + + public Table( + String tableName, + String description, + List originColumnList, + List columnList + ) { + this.tableName = tableName; + this.description = description; + this.originColumnList = originColumnList; + this.columnList = columnList; + } +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/DbService.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/DbService.java new file mode 100644 index 0000000..48a538b --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/DbService.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.service; + +import com.zerolinck.passiflora.codegen.model.Table; + +/** + * @author linck + * @since 2024-02-06 + */ +public interface DbService { + Table getTableInfo(String tableName); +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/RenderService.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/RenderService.java new file mode 100644 index 0000000..9aa0a24 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/RenderService.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.service; + +import com.zerolinck.passiflora.codegen.model.Render; + +/** + * @author linck + * @since 2024-02-07 + */ +public interface RenderService { + void render(Render render); +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/FileRenderServiceImpl.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/FileRenderServiceImpl.java new file mode 100644 index 0000000..02e36cd --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/FileRenderServiceImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.extra.template.Template; +import cn.hutool.extra.template.TemplateConfig; +import cn.hutool.extra.template.TemplateEngine; +import cn.hutool.extra.template.TemplateUtil; +import com.zerolinck.passiflora.codegen.model.Render; +import com.zerolinck.passiflora.codegen.service.RenderService; +import lombok.extern.slf4j.Slf4j; + +/** + * @author linck + * @since 2024-02-07 + */ +@Slf4j +public class FileRenderServiceImpl implements RenderService { + + @Override + public void render(Render render) { + TemplateEngine engine = TemplateUtil.createEngine( + new TemplateConfig( + "templates", + TemplateConfig.ResourceMode.CLASSPATH + ) + ); + Template template = engine.getTemplate(render.getTemplate()); + String result = template.render(render.getData()); + String currentDirectory = System.getProperty("user.dir"); + String dirPath = String.format(currentDirectory + render.getPath()); + String filePath = String.format( + (dirPath + render.getFileName()), + render.getData().get("entityClass") + ); + if (!FileUtil.exist(dirPath)) { + FileUtil.mkdir(dirPath); + } + if (!render.isOverride()) { + if (FileUtil.exist(filePath)) { + log.info("{}文件已存在,不进行写入", filePath); + } else { + FileUtil.writeUtf8String(result, filePath); + } + } else { + if (FileUtil.exist(filePath)) { + log.info("{}文件已存在,覆盖", filePath); + } + FileUtil.writeUtf8String(result, filePath); + } + } +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/PostgresDbService.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/PostgresDbService.java new file mode 100644 index 0000000..baa6897 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/service/impl/PostgresDbService.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.db.Db; +import cn.hutool.db.Entity; +import com.zerolinck.passiflora.codegen.model.Column; +import com.zerolinck.passiflora.codegen.model.Table; +import com.zerolinck.passiflora.codegen.service.DbService; +import com.zerolinck.passiflora.codegen.util.TypeConvert; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import lombok.SneakyThrows; + +/** + * @author linck + * @since 2024-02-06 + */ +public class PostgresDbService implements DbService { + + @Override + @SneakyThrows + public Table getTableInfo(String tableName) { + List result = Db + .use() + .query( + """ + SELECT A + .attname AS COLUMN_NAME, + format_type ( A.atttypid, A.atttypmod ) AS field_type, + A.attnotnull AS NULLABLE, + i.indisprimary AS pk, + d.character_maximum_length AS LENGTH, + col_description ( A.attrelid, A.attnum ) AS description, + d.column_default\s + FROM + pg_class + C INNER JOIN pg_attribute A ON A.attrelid = C. + OID LEFT JOIN pg_index i ON C.OID = i.indrelid\s + AND i.indisprimary = TRUE\s + AND A.attnum = ANY ( i.indkey ) + LEFT JOIN ( SELECT col.COLUMN_NAME, col.column_default, character_maximum_length FROM information_schema.COLUMNS AS col WHERE col.TABLE_NAME = ? ) AS d ON A.attname = d.COLUMN_NAME\s + WHERE + C.relname = ?\s + AND A.attnum > 0; + """, + tableName, + tableName + ); + // 标识是否应该继承 BaseEntity + Dict baseFlag = Dict + .create() + .set("create_by", 0) + .set("update_by", 0) + .set("create_time", 0) + .set("update_time", 0) + .set("del_flag", 0) + .set("version", 0); + AtomicInteger baseFlagNum = new AtomicInteger(); + result.forEach(entity -> { + if (baseFlag.containsKey(entity.get("column_name", ""))) { + baseFlagNum.getAndIncrement(); + } + }); + + List columnList = new ArrayList<>(); + List originColumnList = new ArrayList<>(); + AtomicReference pkFiledName = new AtomicReference<>(); + AtomicReference pkColumnName = new AtomicReference<>(); + result.forEach(entity -> { + Column column = new Column(); + column.setColumnName(entity.get("column_name", "")); + column.setFieldName( + StrUtil.toCamelCase(entity.get("column_name", "")) + ); + column.setColumnType(entity.get("field_type", "")); + column.setFieldType( + TypeConvert.columnType2FieldType(entity.get("field_type", "")) + ); + column.setNullable(entity.get("nullable", true)); + column.setPk(entity.get("pk", false)); + column.setLength(entity.get("length", -1)); + column.setDescription(entity.get("description", "")); + column.setColumnDefault(entity.get("column_default", "")); + if (column.getPk()) { + pkFiledName.set(StrUtil.toCamelCase(column.getColumnName())); + pkColumnName.set(column.getColumnName()); + } + originColumnList.add(column); + if ( + baseFlagNum.get() == baseFlag.size() && + baseFlag.containsKey(entity.get("column_name", "")) + ) { + return; + } + columnList.add(column); + }); + + List tableInfo = Db + .use() + .query("SELECT obj_description(?::regclass);", tableName); + String tableDesc = ""; + for (Entity t : tableInfo) { + tableDesc = t.get("obj_description", ""); + } + return new Table( + tableName, + pkFiledName.get(), + pkColumnName.get(), + tableDesc, + originColumnList, + columnList, + baseFlagNum.get() == baseFlag.size() + ); + } +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/util/TypeConvert.java b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/util/TypeConvert.java new file mode 100644 index 0000000..e8e2c0a --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/java/com/zerolinck/passiflora/codegen/util/TypeConvert.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.codegen.util; + +import com.fasterxml.jackson.databind.JsonNode; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @author linck + * @since 2024-02-06 + */ +public class TypeConvert { + + public static Class columnType2FieldType(String columnType) { + if (columnType.startsWith("character varying")) { + return String.class; + } else if (columnType.startsWith("text")) { + return String.class; + } else if (columnType.startsWith("timestamp")) { + return LocalDateTime.class; + } else if (columnType.startsWith("smallint")) { + return Integer.class; + } else if (columnType.startsWith("integer")) { + return Integer.class; + } else if (columnType.startsWith("bigint")) { + return Long.class; + } else if (columnType.startsWith("json")) { + return JsonNode.class; + } else if (columnType.startsWith("boolean")) { + return Boolean.class; + } else if (columnType.startsWith("numeric")) { + return BigDecimal.class; + } else if (columnType.startsWith("date")) { + return LocalDate.class; + } + throw new RuntimeException("不支持的类型" + columnType); + } +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/db.setting b/passiflora-server/modules/passiflora-codegen/src/main/resources/db.setting new file mode 100644 index 0000000..cddbf93 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/db.setting @@ -0,0 +1,3 @@ +url = jdbc:postgresql://127.0.0.1:5432/passiflora_storage?reWriteBatchedInserts=true +user = postgres +pass = postgres \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/api.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/api.java.ftl new file mode 100644 index 0000000..5e034ac --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/api.java.ftl @@ -0,0 +1,45 @@ +package com.zerolinck.passiflora.feign.${moduleName}; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.${moduleName}.entity.${entityClass}; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author linck + * @since ${date} + */ +@Tag(name = "${table.description}") +@FeignClient(value = "${entityName}", contextId = "${entityName}", path="${contextPath}/${entityName}", configuration = FeignConfiguration.class) +public interface ${apiClass} { + + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page(QueryCondition<${entityClass}> condition); + + @Operation(summary = "新增") + @PostMapping("add") + Result add(@RequestBody @Validated(Insert.class) ${entityClass} ${entityName}); + + @Operation(summary = "更新") + @PostMapping("update") + Result update(@RequestBody @Validated(Update.class) ${entityClass} ${entityName}); + + @Operation(summary = "详情") + @GetMapping("detail") + Result<${entityClass}> detail(@RequestParam(value = "${table.pkFieldName}") String ${table.pkFieldName}); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List ${table.pkFieldName}s); +} \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/controller.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/controller.java.ftl new file mode 100644 index 0000000..d6e8221 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/controller.java.ftl @@ -0,0 +1,67 @@ +package com.zerolinck.passiflora.${moduleName}.controller; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.${moduleName}.${apiClass}; +import com.zerolinck.passiflora.model.${moduleName}.entity.${entityClass}; +import com.zerolinck.passiflora.${moduleName}.service.${serviceClass}; +import lombok.extern.slf4j.Slf4j; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * ${table.description} Controller + * + * @author linck + * @since ${date} + */ +@Slf4j +@RestController +@RequestMapping("${entityName}") +public class ${controllerClass} implements ${apiClass} { + + @Resource + private ${serviceClass} ${serviceName}; + + @Override + public Result> page(QueryCondition<${entityClass}> condition) { + return Result.page(${serviceName}.page(condition)); + } + + @Override + public Result add(${entityClass} ${entityName}) { + ${entityName}.set${table.pkFieldName[0..0]?upper_case}${table.pkFieldName[1..]}(null); + ${serviceName}.add(${entityName}); + return Result.ok(${entityName}.get${table.pkFieldName[0..0]?upper_case}${table.pkFieldName[1..]}()); + } + + @Override + public Result update(${entityClass} ${entityName}) { + boolean success = ${serviceName}.update(${entityName}); + if (success) { + return Result.ok(${entityName}.get${table.pkFieldName[0..0]?upper_case}${table.pkFieldName[1..]}()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result<${entityClass}> detail(String ${table.pkFieldName}) { + AssertUtil.notBlank(${table.pkFieldName}); + return Result.ok(${serviceName}.detail(${table.pkFieldName})); + } + + @Override + public Result delete(List ${table.pkFieldName}s) { + AssertUtil.notEmpty(${table.pkFieldName}s); + ${serviceName}.deleteByIds(${table.pkFieldName}s); + return Result.ok(); + } + +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/convert.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/convert.java.ftl new file mode 100644 index 0000000..18169c0 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/convert.java.ftl @@ -0,0 +1,18 @@ +package com.zerolinck.passiflora.model.${moduleName}.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * ${table.description} MapStruct Convert + * + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface ${entityClass}Convert { + + ${entityClass}Convert INSTANCE = Mappers.getMapper(${entityClass}Convert.class); + +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/entity.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/entity.java.ftl new file mode 100644 index 0000000..7dd2c11 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/entity.java.ftl @@ -0,0 +1,51 @@ +package com.zerolinck.passiflora.model.${moduleName}.entity; + +<#if table.extentBase> +import com.zerolinck.passiflora.model.common.BaseEntity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * ${table.description} Entity + * + * @author linck + * @since ${date} + */ +@Data +@Schema(description = "${table.description}") +<#if table.extentBase> +@EqualsAndHashCode(callSuper = false) + +public class ${entityClass} <#if table.extentBase>extends BaseEntity { + +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.columnList as column> + <#if column.pk> + @TableId(type = IdType.ASSIGN_ID) + + <#if column.fieldType.getSimpleName() == "String" && column.length??> + @Schema(description = "${column.description}", maxLength = ${column.length}) + @Length(groups = {Insert.class, Update.class}, max = ${column.length}, message = "${column.description}长度不能大于${column.length}") + <#else> + @Schema(description = "${column.description}") + + <#if column.nullable> + <#if column.fieldType.getSimpleName() == "String"> + @NotBlank(groups = {<#if column.pk>Update.class<#else>Insert.class, Update.class}, message = "${column.description}不能为空") + <#else> + @NotNull(groups = {Insert.class, Update.class}, message = "${column.description}不能为空") + + + private ${column.fieldType.getSimpleName()} ${column.fieldName}; + + +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.java.ftl new file mode 100644 index 0000000..00aa7d0 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.java.ftl @@ -0,0 +1,29 @@ +package com.zerolinck.passiflora.${moduleName}.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.${moduleName}.entity.${entityClass}; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; + +/** + * ${table.description} Mybatis Mapper + * + * @author linck + * @since ${date} + */ +public interface ${mapperClass} extends BaseMapper<${entityClass}> { + + Page<${entityClass}> page(IPage<${entityClass}> page, + @Param(Constants.WRAPPER) QueryWrapper<${entityClass}> searchWrapper, + @Param("sortWrapper") QueryWrapper<${entityClass}> sortWrapper); + + /** + * 使用更新删除,保证 update_by 和 update_time 正确 + */ + int deleteByIds(@Param("${table.pkFieldName}s") Collection ${table.pkFieldName}s, @Param("updateBy") String updateBy); +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.xml.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.xml.ftl new file mode 100644 index 0000000..897e0ad --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/mapper.xml.ftl @@ -0,0 +1,36 @@ + + + + + + + <#list table.originColumnList as column>${column.columnName?trim}<#if column_has_next>, + + + + + + UPDATE ${tableName} SET update_time = now(), + update_by = ${'#'}{updateBy}, + del_flag = 1 + WHERE ${table.pkColumnName} IN + + ${'#'}{id} + + + + \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/service.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/service.java.ftl new file mode 100644 index 0000000..19d8a0c --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/service.java.ftl @@ -0,0 +1,78 @@ +package com.zerolinck.passiflora.${moduleName}.service; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.${moduleName}.entity.${entityClass}; +import com.zerolinck.passiflora.${moduleName}.mapper.${mapperClass}; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.exception.BizException; +import lombok.extern.slf4j.Slf4j; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; + + +/** + * ${table.description} Service + * + * @author linck + * @since ${date} + */ +@Slf4j +@Service +public class ${serviceClass} extends ServiceImpl<${mapperClass}, ${entityClass}> { + + private static final String LOCK_KEY = "passiflora:lock:${entityName}:"; + + public Page<${entityClass}> page(QueryCondition<${entityClass}> condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page(condition.page(), condition.searchWrapper(${entityClass}.class), condition.sortWrapper(${entityClass}.class)); + } + + public void add(${entityClass} ${entityName}) { + LockUtil.lockAndTransactionalLogic(LOCK_KEY, + new LockWrapper<${entityClass}>(), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, ${entityName}); + baseMapper.insert(${entityName}); + return null; + } + ); + } + + public boolean update(${entityClass} ${entityName}) { + return (boolean) LockUtil.lockAndTransactionalLogic(LOCK_KEY, + new LockWrapper<${entityClass}>(), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, ${entityName}); + int changeRowCount = baseMapper.updateById(${entityName}); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection ${table.pkFieldName}s) { + return baseMapper.deleteByIds(${table.pkFieldName}s, CurrentUtil.getCurrentUserId()); + } + + public ${entityClass} detail(String ${table.pkFieldName}) { + ${entityClass} ${entityName} = baseMapper.selectById(${table.pkFieldName}); + if (${entityName} == null) { + throw new BizException("无对应${table.description}数据,请刷新后重试"); + } + return ${entityName}; + } + +} diff --git a/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/test.java.ftl b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/test.java.ftl new file mode 100644 index 0000000..7584499 --- /dev/null +++ b/passiflora-server/modules/passiflora-codegen/src/main/resources/templates/test.java.ftl @@ -0,0 +1,109 @@ +package com.zerolinck.passiflora.${moduleName}; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.${moduleName}.entity.${entityClass}; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +/** + * ${table.description} Unit Test + * + * @author linck + * @since ${date} + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ${entityClass}ControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String test${entityClass}Id; + + private static ${entityClass} test${entityClass}; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc.perform(get("/${entityName}/page") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode()))); + } + + @Test + @Order(2) + public void testAdd() throws Exception { + ${entityClass} ${entityName} = new ${entityClass}(); + <#list table.columnList as column> + <#if column.nullable> + <#if column.fieldType.getSimpleName() == "String"> + ${entityName}.set${column.fieldName[0..0]?upper_case}${column.fieldName[1..]}("test"); + + + + mockMvc.perform(post("/${entityName}/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(${entityName}))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode()))) + .andDo(result -> test${entityClass}Id = objectMapper.readTree(result.getResponse().getContentAsString()).get("data").asText()); + } + + @Test + @Order(3) + public void testDetail() throws Exception { + mockMvc.perform(get("/${entityName}/detail") + .param("${table.pkFieldName}", test${entityClass}Id)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode()))) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + test${entityClass} = objectMapper.convertValue(jsonNode.get("data"), ${entityClass}.class); + }); + } + + @Test + @Order(4) + public void testUpdate() throws Exception { + mockMvc.perform(post("/${entityName}/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(test${entityClass}))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode()))); + } + + @Test + @Order(5) + public void testDelete() throws Exception { + mockMvc.perform(post("/${entityName}/delete") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new String[]{test${entityClass}Id}))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode()))); + } + +} diff --git a/passiflora-server/modules/passiflora-common/build.gradle b/passiflora-server/modules/passiflora-common/build.gradle new file mode 100644 index 0000000..e9f7b83 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'java-library' +} + +dependencies { + api project(':modules:passiflora-model') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + + compileOnlyApi 'org.springframework:spring-context' + compileOnlyApi 'com.fasterxml.jackson.core:jackson-databind' + compileOnlyApi 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + compileOnlyApi 'com.fasterxml.jackson.core:jackson-core' + compileOnlyApi 'cn.hutool:hutool-core' + compileOnlyApi 'org.springframework.boot:spring-boot-autoconfigure' + compileOnlyApi 'org.springframework:spring-web' + compileOnlyApi 'org.apache.tomcat.embed:tomcat-embed-core' + compileOnlyApi "org.postgresql:postgresql" + compileOnly 'io.swagger.core.v3:swagger-annotations-jakarta' + api 'org.springframework.boot:spring-boot-starter-log4j2' + api 'org.springframework:spring-webmvc' + api 'org.springframework.data:spring-data-redis' + api 'com.baomidou:mybatis-plus-extension' + api 'org.redisson:redisson' +} \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/IErrorCode.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/IErrorCode.java new file mode 100644 index 0000000..e06164c --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/IErrorCode.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.api; + +/** + * 封装API的错误码 + * + * @author linck + */ +public interface IErrorCode { + /** 获取错误码 */ + int getCode(); + + /** 获取message */ + String getMessage(); +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ListWithPage.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ListWithPage.java new file mode 100644 index 0000000..0146ae4 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ListWithPage.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.api; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * List包含总条数信息 + * + * @author linck + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ListWithPage { + + /** 数据列表 */ + private List list; + + /** 总条数 */ + private Long total; +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/Result.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/Result.java new file mode 100644 index 0000000..c092d62 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/Result.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.api; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.Data; + +/** + * 通用返回对象 + * + * @author linck + */ +@Data +public class Result { + + private long code; + private String message; + private T data; + + protected Result() {} + + protected Result(long code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + /** 成功返回结果 */ + public static Result ok() { + return new Result<>( + ResultCodeEnum.SUCCESS.getCode(), + ResultCodeEnum.SUCCESS.getMessage(), + "" + ); + } + + /** + * 成功返回结果 + * + * @param data 获取的数据 + */ + public static Result ok(T data) { + return new Result<>( + ResultCodeEnum.SUCCESS.getCode(), + ResultCodeEnum.SUCCESS.getMessage(), + data + ); + } + + public static Result> page(Page data) { + return new Result<>( + ResultCodeEnum.SUCCESS.getCode(), + ResultCodeEnum.SUCCESS.getMessage(), + new ListWithPage<>(data.getRecords(), data.getTotal()) + ); + } + + /** + * 成功返回结果 + * + * @param data 获取的数据 + * @param message 提示信息 + */ + public static Result ok(T data, String message) { + return new Result<>(ResultCodeEnum.SUCCESS.getCode(), message, data); + } + + /** + * 失败返回结果 + * + * @param errorCode 错误码 + */ + public static Result failed(IErrorCode errorCode) { + return new Result<>(errorCode.getCode(), errorCode.getMessage(), null); + } + + public static Result failed(Integer code, String message) { + return new Result<>(code, message, null); + } + + /** + * 失败返回结果 + * + * @param message 提示信息 + */ + public static Result failed(String message) { + return new Result<>(ResultCodeEnum.FAILED.getCode(), message, null); + } + + /** + * 参数验证失败返回结果 + * + * @param message 提示信息 + */ + public static Result validateFailed(String message) { + return new Result<>( + ResultCodeEnum.VALIDATE_FAILED.getCode(), + message, + null + ); + } + + /** 未登录返回结果 */ + public static Result unauthorized() { + return failed(ResultCodeEnum.UNAUTHORIZED); + } + + /** 未授权返回结果 */ + public static Result forbidden() { + return failed(ResultCodeEnum.FORBIDDEN); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ResultCodeEnum.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ResultCodeEnum.java new file mode 100644 index 0000000..5368fe3 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/api/ResultCodeEnum.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.api; + +/** + * 枚举了一些常用API操作码 + * + * @author linck + */ +public enum ResultCodeEnum implements IErrorCode { + SUCCESS(200, "操作成功"), + FAILED(500, "操作失败"), + NO_STATUS_VALUE(501, "没有对应的状态"), + NO_RESULT_VALUE(502, "没有对应的结果"), + VALIDATE_FAILED(503, "参数检验失败"), + /** 加锁失败 */ + COMPETE_FAILED(504, "操作失败,请刷新重试"), + UNAUTHORIZED(401, "暂未登录或token已经过期"), + FORBIDDEN(403, "没有相关权限"), + NOT_FOUND(404, "404 NOT FOUND"); + + private final int code; + private final String message; + + ResultCodeEnum(int code, String message) { + this.code = code; + this.message = message; + } + + /** + * 通过状态码获取枚举对象 + * + * @param code 状态码 + * @return 枚举对象 + */ + public static ResultCodeEnum getByCode(int code) { + for (ResultCodeEnum resultEnum : ResultCodeEnum.values()) { + if (code == resultEnum.getCode()) { + return resultEnum; + } + } + return null; + } + + @Override + public int getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/CacheConfig.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/CacheConfig.java new file mode 100644 index 0000000..a25679a --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/CacheConfig.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.util.RedisUtils; +import java.time.Duration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.*; + +/** + * Spring Cache 配置 + * + * @author linck + */ +@Configuration +@EnableCaching +@ConditionalOnProperty( + prefix = "passiflora.config", + name = "cache", + havingValue = "true" +) +public class CacheConfig { + + @Bean + public CacheManager cacheManager( + RedisConnectionFactory redisConnectionFactory, + ObjectMapper objectMapper + ) { + RedisCacheConfiguration redisCacheConfiguration = + RedisCacheConfiguration.defaultCacheConfig(); + redisCacheConfiguration = + redisCacheConfiguration + // 设置分隔符 + .computePrefixWith(name -> name + ":") + // 设置key序列化器 + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer() + ) + ) + // 设置value序列化器 + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer(objectMapper) + ) + ) + .entryTtl(Duration.ofDays(7)); + return RedisCacheManager + .builder( + RedisCacheWriter.nonLockingRedisCacheWriter( + redisConnectionFactory + ) + ) + .cacheDefaults(redisCacheConfiguration) + .build(); + } + + @Bean + public RedisTemplate redisTemplate( + RedisConnectionFactory factory, + ObjectMapper objectMapper + ) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + Jackson2JsonRedisSerializer jsonRedisSerializer = + new Jackson2JsonRedisSerializer<>(objectMapper, Object.class); + redisTemplate.setKeySerializer(RedisSerializer.string()); + redisTemplate.setHashKeySerializer(RedisSerializer.string()); + redisTemplate.setValueSerializer(jsonRedisSerializer); + redisTemplate.setHashValueSerializer(jsonRedisSerializer); + redisTemplate.setConnectionFactory(factory); + // 注入工具类中 + RedisUtils.setRedisTemplate(redisTemplate); + return redisTemplate; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/GlobalFilter.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/GlobalFilter.java new file mode 100644 index 0000000..4c1eac8 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/GlobalFilter.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config; + +import com.zerolinck.passiflora.common.util.CurrentUtil; +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.core.annotation.Order; + +/** + * @author linck + * @since 2024-04-24 + */ +@Slf4j +@WebFilter("/*") +@Order(Integer.MIN_VALUE) +@ConditionalOnProperty( + prefix = "passiflora.config", + name = "globalFilter", + havingValue = "true" +) +public class GlobalFilter implements Filter { + + @Override + public void doFilter( + ServletRequest servletRequest, + ServletResponse servletResponse, + FilterChain filterChain + ) throws IOException, ServletException { + CurrentUtil.clear(); + filterChain.doFilter(servletRequest, servletResponse); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/LockConfig.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/LockConfig.java new file mode 100644 index 0000000..8ff0558 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/LockConfig.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config; + +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import org.redisson.api.RedissonClient; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.support.TransactionTemplate; + +/** + * @author linck + * @since 2024-05-13 + */ +@Configuration +@ConditionalOnProperty( + prefix = "passiflora.config", + name = "lock", + havingValue = "true" +) +public class LockConfig { + + @Resource + private ApplicationContext applicationContext; + + @PostConstruct + public void init() { + LockUtil.setRedissonClient( + applicationContext.getBean(RedissonClient.class) + ); + LockUtil.setTransactionTemplate( + applicationContext.getBean(TransactionTemplate.class) + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/PassifloraProperties.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/PassifloraProperties.java new file mode 100644 index 0000000..05bfd96 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/PassifloraProperties.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author linck + * @since 2024-05-27 + */ +@Data +@ConfigurationProperties(prefix = "passiflora") +public class PassifloraProperties { + + /** + * 环境 dev or prod + */ + private String env; + + /** + * 项目版本 + */ + private String projectVersion; + + private System system; + + private Config config; + + private Storage storage; + + @Data + @NoArgsConstructor + public static class System { + + private Token token; + + @Data + @NoArgsConstructor + public static class Token { + + /** + * token 过期时间,单位:s + */ + private Integer expire; + } + } + + @Data + @NoArgsConstructor + public static class Config { + + /** + * 是否开启 MybatisConfig 默认配置 {@link com.zerolinck.passiflora.common.config.mybaits.MybatisConfig} + */ + private Boolean mybatis; + /** + * 是否开启全局异常捕获 {@link com.zerolinck.passiflora.common.handler.GlobalExceptionHandler} + */ + private Boolean exception; + /** + * 是否开启全局默认过滤器 {@link com.zerolinck.passiflora.common.config.GlobalFilter} + */ + private Boolean globalFilter; + /** + * 是否开启 LockUtil 初始化 {@link com.zerolinck.passiflora.common.config.LockConfig} + */ + private Boolean lock; + /** + * 是否开启 Cache 初始化 {@link com.zerolinck.passiflora.common.config.CacheConfig} + */ + private Boolean cache; + } + + @Data + @NoArgsConstructor + public static class Storage { + + /** + * 对象存储桶名称 + */ + private String bucketName; + + private Minio minio; + + @Data + @NoArgsConstructor + public static class Minio { + + /** + * minio 地址 + */ + private String endpoint; + /** + * minio accessKey + */ + private String accessKey; + /** + * minio secretKey + */ + private String secretKey; + } + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/JacksonConfig.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/JacksonConfig.java new file mode 100644 index 0000000..8f41f74 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/JacksonConfig.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.jackson; + +import cn.hutool.core.util.ClassUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import com.zerolinck.passiflora.common.util.EnumUtil; +import com.zerolinck.passiflora.common.util.TimeUtil; +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author linck + * @since 2024-02-06 + */ +@Configuration +public class JacksonConfig { + + @Bean + @SuppressWarnings("unchecked") + public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { + Map, JsonSerializer> serializers = new HashMap<>(); + serializers.put( + LocalDateTime.class, + new LocalDateTimeSerializer(TimeUtil.NORMAL_DATE_TIME_FORMATTER) + ); + serializers.put( + LocalDate.class, + new LocalDateSerializer(TimeUtil.NORMAL_DATE_FORMATTER) + ); + serializers.put( + LocalTime.class, + new LocalTimeSerializer(TimeUtil.NORMAL_TIME_FORMATTER_NO_SECOND) + ); + + // NameValue 序列化配置 + serializers.put( + LabelValueInterface.class, + new JsonSerializer() { + @Override + public void serialize( + LabelValueInterface value, + JsonGenerator jsonGenerator, + SerializerProvider serializers + ) throws IOException { + jsonGenerator.writeObject(value.getValue()); + } + } + ); + + Map, JsonDeserializer> deserializers = new HashMap<>(); + deserializers.put(LocalDateTime.class, new LocalDateTimeDeserializer()); + deserializers.put( + LocalDate.class, + new LocalDateDeserializer(TimeUtil.NORMAL_DATE_FORMATTER) + ); + deserializers.put( + LocalTime.class, + new LocalTimeDeserializer(TimeUtil.NORMAL_TIME_FORMATTER_NO_SECOND) + ); + + // 扫描当前项目下所有 LabelValueInterface 实现类 + Set> classes = ClassUtil.scanPackage( + "com.zerolinck", + aClass -> + LabelValueInterface.class.isAssignableFrom(aClass) && + !LabelValueInterface.class.equals(aClass) + ); + + // 自动注册枚举反序列化规则 + classes.forEach(clazz -> + deserializers.put( + clazz, + new JsonDeserializer() { + @Override + public Object deserialize( + JsonParser jsonParser, + DeserializationContext ctxt + ) throws IOException { + Integer value = jsonParser.getIntValue(); + return EnumUtil.getEnumByValue( + (Class) clazz, + value + ); + } + } + ) + ); + + return builder -> + builder + .serializersByType(serializers) + .deserializersByType(deserializers); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/LocalDateTimeDeserializer.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/LocalDateTimeDeserializer.java new file mode 100644 index 0000000..d837a84 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/jackson/LocalDateTimeDeserializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.zerolinck.passiflora.common.util.TimeUtil; +import java.io.IOException; +import java.time.LocalDateTime; + +/** + * @author linck + * @since 2024-02-06 + */ +public class LocalDateTimeDeserializer + extends com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer { + + public LocalDateTimeDeserializer() { + super(TimeUtil.NORMAL_DATE_TIME_FORMATTER); + } + + @Override + public LocalDateTime deserialize( + JsonParser parser, + DeserializationContext context + ) throws IOException { + String timeStr = parser.getText(); + return TimeUtil.commonStrDate2LocalDateTime(timeStr); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/DefaultDBFieldHandler.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/DefaultDBFieldHandler.java new file mode 100644 index 0000000..865b189 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/DefaultDBFieldHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.mybaits; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import java.nio.charset.Charset; +import java.time.LocalDateTime; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.property.PropertyTokenizer; +import org.springframework.util.ClassUtils; + +/** + * @author linck + * @since 2024-02-07 + */ +@Slf4j +public class DefaultDBFieldHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + log.debug("mybatis plus start insert fill ...."); + SysUser sysUser = CurrentUtil.getCurrentUser(); + LocalDateTime now = LocalDateTime.now(); + + fillValIfNullByName("createTime", now, metaObject, true); + fillValIfNullByName("updateTime", now, metaObject, true); + fillValIfNullByName("delFlag", 0, metaObject, true); + if (sysUser != null) { + fillValIfNullByName( + "createBy", + sysUser.getUserId(), + metaObject, + true + ); + fillValIfNullByName( + "updateBy", + sysUser.getUserId(), + metaObject, + true + ); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + log.debug("mybatis plus start update fill ...."); + LocalDateTime now = LocalDateTime.now(); + SysUser sysUser = CurrentUtil.getCurrentUser(); + fillValIfNullByName("updateTime", now, metaObject, true); + if (sysUser != null) { + fillValIfNullByName( + "updateBy", + sysUser.getUserId(), + metaObject, + true + ); + } + } + + /** + * 填充值,先判断是否有手动设置,优先手动设置的值 + * + * @param fieldName 属性名 + * @param fieldVal 属性值 + * @param metaObject MetaObject + * @param isCover 是否覆盖原有值,避免更新操作手动入参 + */ + private static void fillValIfNullByName( + String fieldName, + Object fieldVal, + MetaObject metaObject, + boolean isCover + ) { + // 1. 没有 set 方法 + if (!metaObject.hasSetter(fieldName)) { + return; + } + // 2. 如果用户有手动设置的值 + Object userSetValue = metaObject.getValue(fieldName); + String setValueStr = StrUtil.str( + userSetValue, + Charset.defaultCharset() + ); + if (StrUtil.isNotBlank(setValueStr) && !isCover) { + return; + } + if (fieldVal == null) { + PropertyTokenizer prop = new PropertyTokenizer(fieldName); + metaObject.getObjectWrapper().set(prop, null); + return; + } + // 3. field 类型相同时设置 + Class getterType = metaObject.getGetterType(fieldName); + if (ClassUtils.isAssignableValue(getterType, fieldVal)) { + metaObject.setValue(fieldName, fieldVal); + } + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/JsonTypeHandler.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/JsonTypeHandler.java new file mode 100644 index 0000000..a9e9734 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/JsonTypeHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.mybaits; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import lombok.SneakyThrows; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.postgresql.util.PGobject; + +/** + * @author linck + * @since 2024-01-11 + */ +public class JsonTypeHandler extends BaseTypeHandler { + + private static final PGobject jsonObject = new PGobject(); + + private ObjectMapper objectMapper; + + public JsonTypeHandler(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public void setNonNullParameter( + PreparedStatement ps, + int i, + JsonNode parameter, + JdbcType jdbcType + ) throws SQLException { + jsonObject.setType("json"); + jsonObject.setValue(parameter.toString()); + ps.setObject(i, jsonObject); + } + + @SneakyThrows + @Override + public JsonNode getNullableResult(ResultSet resultSet, String s) { + return objectMapper.readValue(resultSet.getString(s), JsonNode.class); + } + + @SneakyThrows + @Override + public JsonNode getNullableResult(ResultSet resultSet, int i) { + return objectMapper.readValue(resultSet.getString(i), JsonNode.class); + } + + @SneakyThrows + @Override + public JsonNode getNullableResult( + CallableStatement callableStatement, + int i + ) { + return objectMapper.readValue( + callableStatement.getString(i), + JsonNode.class + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/MybatisConfig.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/MybatisConfig.java new file mode 100644 index 0000000..e5516a3 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/MybatisConfig.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.mybaits; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author linck + * @since 2024-02-07 + */ +@Configuration +@ConditionalOnProperty( + prefix = "passiflora.config", + name = "mybatis", + havingValue = "true" +) +public class MybatisConfig { + + /** 添加分页插件 */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor( + new PaginationInnerInterceptor(DbType.POSTGRE_SQL) + ); + return interceptor; + } + + /** + * 乐观锁插件 整数类型下 newVersion = oldVersion + 1 newVersion 会回写到 entity 中 仅支持 updateById(id) 与 + * update(entity, wrapper) 方法 在 update(entity, wrapper) 方法下, wrapper 不能复用!!! + */ + @Bean + public MybatisPlusInterceptor optimisticLockerInnerInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } + + /** 阻止恶意的全表更新删除 */ + @Bean + public MybatisPlusInterceptor blockAttackInnerInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + return interceptor; + } + + @Bean + public DefaultDBFieldHandler defaultDBFieldHandler() { + return new DefaultDBFieldHandler(); + } + + @Bean + public TypeHandlerRegister enumHandler() { + return new TypeHandlerRegister(); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/TypeHandlerRegister.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/TypeHandlerRegister.java new file mode 100644 index 0000000..1ff2bac --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/config/mybaits/TypeHandlerRegister.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.config.mybaits; + +import cn.hutool.core.util.ClassUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.util.EnumUtil; +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Set; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +/** + * @author linck + * @since 2023-04-11 + */ +public class TypeHandlerRegister { + + @Resource + SqlSessionFactory sqlSessionFactory; + + @Resource + ObjectMapper objectMapper; + + @PostConstruct + @SuppressWarnings("unchecked") + public void init() { + // 扫描当前项目下所有 LabelValueInterface 实现类 + Set> classes = ClassUtil.scanPackage( + "com.zerolinck", + aClass -> + LabelValueInterface.class.isAssignableFrom(aClass) && + !LabelValueInterface.class.equals(aClass) + ); + + sqlSessionFactory + .getConfiguration() + .getTypeHandlerRegistry() + .register(JsonNode.class, new JsonTypeHandler(objectMapper)); + + // 注册枚举类与数据库查询结果/参数的序列化与反序列化规则 + classes.forEach(clazz -> + sqlSessionFactory + .getConfiguration() + .getTypeHandlerRegistry() + .register( + clazz, + new BaseTypeHandler() { + @Override + public void setNonNullParameter( + PreparedStatement ps, + int i, + Object parameter, + JdbcType jdbcType + ) throws SQLException { + ps.setInt( + i, + ((LabelValueInterface) parameter).getValue() + ); + } + + @Override + public Object getNullableResult( + ResultSet rs, + String columnName + ) throws SQLException { + return EnumUtil.getEnumByValue( + (Class) clazz, + rs.getInt(columnName) + ); + } + + @Override + public Object getNullableResult( + ResultSet rs, + int columnIndex + ) throws SQLException { + return EnumUtil.getEnumByValue( + (Class) clazz, + rs.getInt(columnIndex) + ); + } + + @Override + public Object getNullableResult( + CallableStatement cs, + int columnIndex + ) throws SQLException { + return EnumUtil.getEnumByValue( + (Class) clazz, + cs.getInt(columnIndex) + ); + } + } + ) + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/exception/BizException.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/exception/BizException.java new file mode 100644 index 0000000..30841c3 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/exception/BizException.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.exception; + +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import lombok.Getter; + +/** + * 自定义运行时异常 + * + * @author linck + */ +@Getter +public class BizException extends RuntimeException { + + /** 状态码 */ + private final Integer code; + + public BizException(BizException e, ResultCodeEnum resultCodeEnum) { + super(e.getMessage()); + this.code = resultCodeEnum.getCode(); + } + + public BizException(BizException e) { + super(e.getMessage()); + this.code = e.getCode(); + } + + public BizException(Throwable throwable, ResultCodeEnum resultCodeEnum) { + super(throwable); + this.code = resultCodeEnum.getCode(); + } + + public BizException(Throwable throwable) { + super(throwable); + this.code = ResultCodeEnum.FAILED.getCode(); + } + + public BizException(String message) { + super(message); + this.code = ResultCodeEnum.FAILED.getCode(); + } + + /** + * 自定义异常 + * + * @param resultCodeEnum 返回枚举对象 + */ + public BizException(ResultCodeEnum resultCodeEnum) { + super(resultCodeEnum.getMessage()); + this.code = resultCodeEnum.getCode(); + } + + /** + * @param resultCodeEnum 返回枚举对象 + * @param message 错误信息 + */ + public BizException(ResultCodeEnum resultCodeEnum, String message) { + super(message); + this.code = resultCodeEnum.getCode(); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/handler/GlobalExceptionHandler.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..4701795 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/handler/GlobalExceptionHandler.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.handler; + +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.NetUtil; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Locale; +import java.util.StringJoiner; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.servlet.resource.NoResourceFoundException; + +/** + * 全局异常捕获 + * + * @author linck + */ +@Slf4j +@RestControllerAdvice +@ConditionalOnProperty( + prefix = "passiflora.config", + name = "exception", + havingValue = "true" +) +public class GlobalExceptionHandler { + + /** 捕获自定义异常 */ + @ExceptionHandler(value = BizException.class) + public Result processException(BizException e) { + log.error("捕获自定义异常", e); + return Result.failed(e.getCode(), e.getMessage()); + } + + /** 捕获异常返回详细异常信息 */ + @ExceptionHandler(Exception.class) + public Result exceptionHandler(Exception e) { + long epochSecond = LocalDateTime + .now() + .atZone(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli(); + String code = Long.toHexString(epochSecond).toUpperCase(Locale.ROOT); + log.error("系统错误,错误代码: 0X{}", code, e); + return Result.failed("系统错误,错误代码:0X" + code); + } + + /** 捕获请求参数错误 */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public Result exceptionHandler(HttpMessageNotReadableException e) { + long epochSecond = LocalDateTime + .now() + .atZone(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli(); + String code = Long.toHexString(epochSecond).toUpperCase(Locale.ROOT); + log.error("系统错误,错误代码: 0X{}", code, e); + return Result.failed(ResultCodeEnum.VALIDATE_FAILED); + } + + @ExceptionHandler(NoResourceFoundException.class) + public Result exceptionHandler(NoResourceFoundException e) { + NetUtil.getResponse().setStatus(404); + log.error("404 Not Found: {}", e.getMessage()); + return Result.failed(ResultCodeEnum.NOT_FOUND); + } + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Result exceptionHandler( + HttpRequestMethodNotSupportedException e + ) { + log.error("RequestMethod 不支持", e); + return Result.failed(e.getMessage()); + } + + /** 捕获异常返回详细异常信息 */ + @ExceptionHandler(MaxUploadSizeExceededException.class) + public Result exceptionHandler(MaxUploadSizeExceededException e) { + log.error("上传文件大小超出系统限制", e); + return Result.failed("上传文件大小超出系统限制"); + } + + /** Post参数验证异常 */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result methodArgumentNotValidExceptionHandler( + MethodArgumentNotValidException e + ) { + // 将所有的错误提示使用","拼接起来并返回 + StringJoiner sj = new StringJoiner(","); + e + .getBindingResult() + .getFieldErrors() + .forEach(x -> sj.add(x.getDefaultMessage())); + log.error("参数错误:{}", sj); + return Result.validateFailed(sj.toString()); + } + + /** get参数验证异常,会抛出一个BindException */ + @ExceptionHandler(BindException.class) + public Result constraintViolationExceptionHandler(BindException e) { + StringJoiner sj = new StringJoiner(","); + e + .getBindingResult() + .getFieldErrors() + .forEach(x -> sj.add(x.getDefaultMessage())); + log.error("参数错误:{}", sj); + return Result.validateFailed(sj.toString()); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/AssertUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/AssertUtil.java new file mode 100644 index 0000000..a658072 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/AssertUtil.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import com.zerolinck.passiflora.common.exception.BizException; +import java.util.Collection; + +/** + * @author linck + * @since 2024-03-18 + */ +public class AssertUtil { + + public static void notEmpty(Collection collection) { + if (collection == null || collection.isEmpty()) { + throw new BizException("参数不能为空"); + } + } + + public static void notBlank(CharSequence charSequence) { + if (charSequence == null || charSequence.isEmpty()) { + throw new BizException("参数不能为空"); + } + } + + public static void notNull(Object object) { + if (object == null) { + throw new BizException("参数不能为空"); + } + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/CurrentUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/CurrentUtil.java new file mode 100644 index 0000000..619bca2 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/CurrentUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.model.common.constant.Constants; +import com.zerolinck.passiflora.model.common.constant.RedisPrefix; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import java.util.Set; + +/** + * @author linck + * @since 2023-12-15 + */ +public class CurrentUtil { + + /** 当前登录账户,线程级别缓存 */ + private static final ThreadLocal userMap = new ThreadLocal<>(); + + /** 当前token */ + private static final ThreadLocal tokenMap = new ThreadLocal<>(); + + /** 获取当前登录用户 */ + public static SysUser getCurrentUser() { + if (userMap.get() != null) { + return userMap.get(); + } + String token = NetUtil.getRequest().getHeader(Constants.Authorization); + Set keys = RedisUtils.keys( + RedisPrefix.TOKEN_KEY + "*:" + token + ); + if (keys == null || keys.isEmpty()) { + return null; + } + Object o = RedisUtils.get(keys.stream().findFirst().get()); + if (o == null) { + return null; + } + ObjectMapper objectMapper = SpringContextHolder.getBean( + ObjectMapper.class + ); + SysUser sysUser = objectMapper.convertValue(o, SysUser.class); + userMap.set(sysUser); + return sysUser; + } + + public static String getCurrentUserId() { + SysUser currentUser = getCurrentUser(); + if (currentUser == null) { + return null; + } + return currentUser.getUserId(); + } + + /** 获取当前 token */ + public static String getToken() { + if (tokenMap.get() != null) { + return tokenMap.get(); + } + String token = NetUtil.getRequest().getHeader(Constants.Authorization); + tokenMap.set(token); + return token; + } + + public static void clear() { + userMap.remove(); + tokenMap.remove(); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/EnumUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/EnumUtil.java new file mode 100644 index 0000000..6578c63 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/EnumUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; + +/** + * @author linck + * @since 2024-02-06 + */ +public class EnumUtil { + + public static LabelValueInterface getEnumByValue( + Class clazz, + Object value + ) { + LabelValueInterface[] enumConstants = clazz.getEnumConstants(); + for (LabelValueInterface nameValue : enumConstants) { + if (nameValue.getValue().equals(value)) { + return nameValue; + } + } + throw new RuntimeException( + clazz.getSimpleName() + " 无此value: " + value + ); + } + + public static LabelValueInterface getEnumByName( + Class clazz, + Object name + ) { + LabelValueInterface[] enumConstants = clazz.getEnumConstants(); + for (LabelValueInterface nameValue : enumConstants) { + if (nameValue.getLabel().equals(name)) { + return nameValue; + } + } + throw new RuntimeException( + clazz.getSimpleName() + " 无此name: " + name + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/NetUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/NetUtil.java new file mode 100644 index 0000000..15128f7 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/NetUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.net.Inet4Address; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Collections; +import java.util.Enumeration; +import java.util.concurrent.atomic.AtomicReference; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * @author linck + * @since 2023-12-11 + */ +public class NetUtil { + + /** 查询网卡 ip 地址 */ + public static String findOutIp() throws SocketException { + Enumeration interfaces = + NetworkInterface.getNetworkInterfaces(); + AtomicReference ip = new AtomicReference<>(null); + for (NetworkInterface face : Collections.list(interfaces)) { + // 过滤回环接口等 + if (face.isLoopback() || !face.isUp()) continue; + + face + .getInterfaceAddresses() + .stream() + .map(InterfaceAddress::getAddress) + .filter(a -> a instanceof Inet4Address && !a.isLoopbackAddress() + ) + .findFirst() + .ifPresent(a -> ip.set(a.getHostAddress())); + } + return ip.get(); + } + + public static HttpServletRequest getRequest() { + ServletRequestAttributes servletRequestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + assert servletRequestAttributes != null; + return servletRequestAttributes.getRequest(); + } + + public static HttpServletResponse getResponse() { + ServletRequestAttributes servletRequestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + assert servletRequestAttributes != null; + return servletRequestAttributes.getResponse(); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/OnlyFieldCheck.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/OnlyFieldCheck.java new file mode 100644 index 0000000..d178a60 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/OnlyFieldCheck.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.model.valid.OnlyField; +import io.swagger.v3.oas.annotations.media.Schema; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; + +/** + * @author linck + * @since 2023-12-18 + */ +public class OnlyFieldCheck { + + private static final Map, List> map = new HashMap<>(); + + @SneakyThrows + public static void checkInsert(BaseMapper baseMapper, Object entity) { + checkCache(entity); + List fields = map.get(entity.getClass()); + for (CheckField field : fields) { + field.getField().setAccessible(true); + + Object fieldValue = field.getField().get(entity); + if (fieldValue == null) { + continue; + } + + Long count = baseMapper.selectCount( + new QueryWrapper<>().eq(field.getFieldName(), fieldValue) + ); + if (count > 0) { + String message; + if (StrUtil.isNotBlank(field.getMessage())) { + message = field.getMessage(); + } else { + message = field.getFieldDesc() + "已存在,请重新填写"; + } + throw new BizException(message); + } + } + } + + @SneakyThrows + public static void checkUpdate(BaseMapper baseMapper, Object entity) { + checkCache(entity); + List fields = map.get(entity.getClass()); + for (CheckField field : fields) { + field.getField().setAccessible(true); + field.getIdField().setAccessible(true); + + Object fieldValue = field.getField().get(entity); + if (fieldValue == null) { + continue; + } + + Long count = baseMapper.selectCount( + new QueryWrapper<>() + .eq(field.getFieldName(), fieldValue) + .ne( + StrUtil.toUnderlineCase(field.getIdField().getName()), + field.getIdField().get(entity) + ) + ); + if (count > 0) { + String message; + if (StrUtil.isNotBlank(field.getMessage())) { + message = field.getMessage(); + } else { + message = field.getFieldDesc() + "已存在,请重新填写"; + } + throw new BizException(message); + } + } + } + + private static void checkCache(Object entity) { + if (!map.containsKey(entity.getClass())) { + Field[] fields = entity.getClass().getDeclaredFields(); + List result = new ArrayList<>(); + Field idField = null; + for (Field field : fields) { + if (field.getAnnotation(TableId.class) != null) { + idField = field; + break; + } + } + for (Field field : fields) { + OnlyField annotation = field.getAnnotation(OnlyField.class); + if (annotation == null) { + continue; + } + + String desc = field.getName(); + + Schema schema = field.getAnnotation(Schema.class); + if (schema != null) { + desc = schema.description(); + } + CheckField checkField = new CheckField( + field, + idField, + StrUtil.toUnderlineCase(field.getName()), + desc, + annotation.message() + ); + result.add(checkField); + } + map.put(entity.getClass(), result); + } + } + + @Data + @AllArgsConstructor + public static class CheckField { + + private Field field; + private Field idField; + private String fieldName; + private String fieldDesc; + private String message; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/QueryCondition.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/QueryCondition.java new file mode 100644 index 0000000..ae6ded1 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/QueryCondition.java @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import io.swagger.v3.oas.annotations.media.Schema; +import java.lang.reflect.Field; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Data; + +/** + * 通用条件构造器,通过 url param 参数设置,使用方式 + * + *

eg: 查询 id = 1 的数据,eq[id]=1 eg: 模糊查询 name 包含张三的数据,like[name]=张三 eg: 左模糊查询 name + * 包含张三的数据,likeL[name]=张三 + * + *

eg: 右模糊查询 name 包含张三的数据,likeR[name]=张三 eg: 模糊查询 name 不包含张三的数据,nLike[name]=张三 + * + *

eg: 范围查询 time 在 2023-01-01 和 2023-02-02 之间的数据, ge[time]=2023-01-01&le[time]=2023-02-02 + * + *

eg: 排序 sort[name]=desc&sort[id]=asc 等同于 order by name desc, id asc + * + * @param 创建 page 以及,queryWrapper 的泛型类 + * @author linck + * @since 2022-11-18 + */ +@Data +public class QueryCondition { + + /** 创建 page 以及,queryWrapper 是泛型对应的具体实体类 */ + @Schema(hidden = true) + private Class entityClazz; + + /** + * 表别名,只作用于实体类字段,不作用于 fieldNameCover 映射 eg: eq[name] = 2,对应到SQL时,不是 name = 2,而应该是 user.name = 2 + * 就可以设置 tableAlise = user + */ + @Schema(hidden = true) + private String tableAlise; + + /** + * 字段名转换 eg:eq[name] = 2,对应到SQL时,不是 name = 2,而应该是 user.display_name = 2 就可以配置 name: + * user.display_name 这样的映射关系 + */ + @Schema(hidden = true) + private Map fieldNameCover; + + /** 字段缓存 外层 map key 为实体类 class 内存 map key 为字段名称 */ + @Schema(hidden = true) + private static final Map, Map> FIELD_CACHE = + new HashMap<>(); + + /** eq == eg: eg[name]=张三 */ + @Schema(hidden = true) + private Map> eq; + + /** neq != eg: ne[name]=张三 */ + @Schema(hidden = true) + private Map> ne; + + /** gt > */ + @Schema(hidden = true) + private Map> gt; + + /** ge >= */ + @Schema(hidden = true) + private Map> ge; + + /** lt < */ + @Schema(hidden = true) + private Map> lt; + + /** le <= */ + @Schema(hidden = true) + private Map> le; + + /** 模糊搜索 eg: like[phone]=138 等同于 phone like '%138%' */ + @Schema(hidden = true) + private Map> like; + + /** 模糊搜索 eg: nLike[phone]=138 等同于 phone not like '%138%' */ + @Schema(hidden = true) + private Map> nLike; + + /** 左模糊 eg: likeL[phone]=138 等同于 phone like '%138' */ + @Schema(hidden = true) + private Map> likeL; + + /** 右模糊 eg: likeR[phone]=138 等同于 phone like '138%' */ + @Schema(hidden = true) + private Map> likeR; + + /** + * 排序 eg: sort[name]=desc 等同于 order by name desc eg: sort[name]=desc&sort[id]=asc 等同于 order by + * name desc, id asc + */ + @Schema(hidden = true) + private Map> sort; + + /** 页数 */ + private Integer current; + + /** 每页行数 */ + private Integer pageSize; + + public QueryCondition() { + eq = new HashMap<>(); + gt = new HashMap<>(); + ge = new HashMap<>(); + lt = new HashMap<>(); + le = new HashMap<>(); + ne = new HashMap<>(); + like = new HashMap<>(); + nLike = new HashMap<>(); + likeL = new HashMap<>(); + likeR = new HashMap<>(); + sort = new HashMap<>(); + } + + /** 开启分页查询,如果没有分页参数,则查询第一页,20行数据,单页最大允许查询 5000 条数据 */ + public Page page() { + if (current == null) { + current = 1; + } + if (pageSize == null) { + pageSize = 20; + } + // 请求允许一次最大查询 5000 条数据 + if (pageSize > 5000) { + pageSize = 5000; + } + return new Page<>(current, pageSize); + } + + /** 设置表别名 */ + public QueryCondition tableAlise(String tableAlise) { + this.tableAlise = tableAlise; + return this; + } + + /** 设置字段名转换映射 */ + public QueryCondition fieldNameCover( + Map fieldNameCover + ) { + this.fieldNameCover = fieldNameCover; + return this; + } + + /** + * 根据 condition 内容创建 queryWrapper column 会优先使用别名转换映射进行转换 如果字段在别名转换映射中不存在,则会通过 clazz + * 获取对应实体类的字段,存在的字段,才允许作为条件 + * + * @param clazz list 查询的实体类 + */ + public QueryWrapper searchWrapper(Class clazz) { + this.entityClazz = clazz; + + QueryWrapper queryWrapper = new QueryWrapper<>(); + this.getEq() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.eq(coverColumn, valueCover(column, value)); + }) + ); + + this.getNe() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.ne(coverColumn, valueCover(column, value)); + }) + ); + + this.getGt() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.gt(coverColumn, valueCover(column, value)); + }) + ); + + this.getGe() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.ge(coverColumn, valueCover(column, value)); + }) + ); + + this.getLt() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.lt(coverColumn, valueCover(column, value)); + }) + ); + + this.getLe() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.le(coverColumn, valueCover(column, value)); + }) + ); + + this.getLike() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.like(coverColumn, valueCover(column, value)); + }) + ); + + this.getNLike() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.notLike( + coverColumn, + valueCover(column, value) + ); + }) + ); + + this.getLikeL() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.likeLeft( + coverColumn, + valueCover(column, value) + ); + }) + ); + + this.getLikeR() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + queryWrapper.likeRight( + coverColumn, + valueCover(column, value) + ); + }) + ); + return queryWrapper; + } + + public QueryWrapper sortWrapper(Class clazz) { + this.entityClazz = clazz; + QueryWrapper sortWrapper = new QueryWrapper<>(); + this.getSort() + .forEach((column, values) -> + values.forEach(value -> { + String coverColumn = fieldCover(column); + if ("desc".equals(value)) { + sortWrapper.orderByDesc(coverColumn); + } else { + sortWrapper.orderByAsc(coverColumn); + } + }) + ); + return sortWrapper; + } + + /** + * 字段转换 + * + * @param column 原字段 + * @return 转换后的字段 + */ + public String fieldCover(String column) { + if ( + this.fieldNameCover != null && + this.fieldNameCover.containsKey(column) + ) { + return this.fieldNameCover.get(column); + } + Map fieldMap = this.getFields(); + if (fieldMap.containsKey(column)) { + // 驼峰转下划线 + return ( + (this.tableAlise == null ? "" : (this.tableAlise + ".")) + + StringUtils.camelToUnderline(column) + ); + } + throw new BizException(String.format("不允许的搜索条件: %s", column)); + } + + /** + * 值数据格式转换,针对 postgres,Mysql 不需要 + * + * @param column 原字段 + * @return 转换后的字段 + */ + public Object valueCover(String column, String value) { + Field field = this.getFields().get(column); + if (field.getType().equals(String.class)) { + return value; + } else if (field.getType().equals(LocalDateTime.class)) { + return TimeUtil.commonStrDate2LocalDateTime(value); + } else if (field.getType().equals(LocalDate.class)) { + return LocalDate.parse(value, TimeUtil.NORMAL_DATE_FORMATTER); + } else if (field.getType().equals(LocalTime.class)) { + return LocalTime.parse( + value, + TimeUtil.NORMAL_TIME_FORMATTER_NO_SECOND + ); + } else if (field.getType().equals(Integer.class)) { + return Integer.valueOf(value); + } else if (field.getType().equals(Long.class)) { + return Long.valueOf(value); + } + Class[] interfaces = field.getType().getInterfaces(); + if (interfaces.length > 0) { + if (interfaces[0].equals(LabelValueInterface.class)) { + return Integer.valueOf(value); + } + } + throw new BizException( + "不支持的搜索类型:" + field.getType().getSimpleName() + ); + } + + /** 获取字段缓存 */ + private Map getFields() { + if (!FIELD_CACHE.containsKey(this.entityClazz)) { + FIELD_CACHE.put(this.entityClazz, getClassFields(this.entityClazz)); + } + return FIELD_CACHE.get(this.entityClazz); + } + + /** + * 初始化实体类字段缓存 + * + * @param clazz 要操作的实体类 class + */ + private static Map getClassFields(Class clazz) { + Map fieldMap = new HashMap<>(); + + // 如果父类不是 Object,获取其父类字段 + Class superclass = clazz.getSuperclass(); + while (!superclass.equals(Object.class)) { + for (Field field : superclass.getDeclaredFields()) { + fieldMap.put(field.getName(), field); + } + superclass = superclass.getSuperclass(); + } + + for (Field field : clazz.getDeclaredFields()) { + fieldMap.put(field.getName(), field); + } + return fieldMap; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/RedisUtils.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/RedisUtils.java new file mode 100644 index 0000000..57b0886 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/RedisUtils.java @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.util.CollectionUtils; + +/** + * 需要再 Configuration 中注册 redisTemplate 时,将 redisTemplate 注入此工具类 + * + * @author linck + * @since 2023-12-14 + */ +@Slf4j +public class RedisUtils { + + private static RedisTemplate redisTemplate; + + public static void setRedisTemplate( + RedisTemplate redisTemplate + ) { + RedisUtils.redisTemplate = redisTemplate; + } + + public static Set keys(String keys) { + try { + return redisTemplate.keys(keys); + } catch (Exception e) { + log.error("redis异常", e); + return null; + } + } + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + */ + public static boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 根据key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public static long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public static boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public static void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete( + (Collection) CollectionUtils.arrayToList(key) + ); + } + } + } + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public static Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public static boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public static boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate + .opsForValue() + .set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + */ + public static long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(小于0) + */ + public static long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public static Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public static Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public static boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public static boolean hmset( + String key, + Map map, + long time + ) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public static boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public static boolean hset( + String key, + String item, + Object value, + long time + ) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public static void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public static boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + */ + public static double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + */ + public static double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + */ + public static Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + log.error("redis异常", e); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public static boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public static long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public static long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) expire(key, time); + return count; + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + */ + public static long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public static long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } + + // ===============================list================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + */ + public static List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + log.error("redis异常", e); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + */ + public static long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + */ + public static Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + log.error("redis异常", e); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + */ + public static boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public static boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) expire(key, time); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + */ + public static boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public static boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) expire(key, time); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + */ + public static boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + log.error("redis异常", e); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public static long lRemove(String key, long count, Object value) { + try { + return redisTemplate.opsForList().remove(key, count, value); + } catch (Exception e) { + log.error("redis异常", e); + return 0; + } + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/SpringContextHolder.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/SpringContextHolder.java new file mode 100755 index 0000000..622bd5b --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/SpringContextHolder.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +/** + * Spring 工具类 + * + * @author lengleng + * @since 2019/2/1 + */ +@Slf4j +@Service +@Lazy(false) +public class SpringContextHolder + implements ApplicationContextAware, DisposableBean { + + /** -- GETTER -- 取得存储在静态变量中的ApplicationContext. */ + @Getter + private static ApplicationContext applicationContext = null; + + /** 实现ApplicationContextAware接口, 注入Context到静态变量中. */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + SpringContextHolder.applicationContext = applicationContext; + } + + /** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + return (T) applicationContext.getBean(name); + } + + /** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ + public static T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + /** 清除SpringContextHolder中的ApplicationContext为Null. */ + public static void clearHolder() { + if (log.isDebugEnabled()) { + log.debug( + "清除SpringContextHolder中的ApplicationContext:" + + applicationContext + ); + } + applicationContext = null; + } + + /** 发布事件 */ + public static void publishEvent(ApplicationEvent event) { + if (applicationContext == null) { + return; + } + applicationContext.publishEvent(event); + } + + /** 实现DisposableBean接口, 在Context关闭时清理静态变量. */ + @Override + @SneakyThrows + public void destroy() { + SpringContextHolder.clearHolder(); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/TimeUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/TimeUtil.java new file mode 100644 index 0000000..8c30a01 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/TimeUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.regex.Pattern; + +/** + * @author linck + * @since 2024-02-06 + */ +public class TimeUtil { + + private static final Pattern NORMAL = Pattern.compile( + "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$" + ); + + private static final Pattern NO_SECOND = Pattern.compile( + "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$" + ); + private static final Pattern NO_MINUTS = Pattern.compile( + "^\\d{4}-\\d{2}-\\d{2} \\d{2}$" + ); + private static final Pattern NO_HOUR = Pattern.compile( + "^\\d{4}-\\d{2}-\\d{2}$" + ); + + public static final DateTimeFormatter NORMAL_DATE_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public static final DateTimeFormatter NORMAL_DATE_TIME_FORMATTER_NO_SECOND = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + + public static final DateTimeFormatter NORMAL_DATE_TIME_FORMATTER_NO_MINUTS = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH"); + + public static final DateTimeFormatter NORMAL_DATE_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public static final DateTimeFormatter NORMAL_TIME_FORMATTER_NO_SECOND = + DateTimeFormatter.ofPattern("HH:mm"); + + public static LocalDateTime commonStrDate2LocalDateTime(String dateStr) { + if (NORMAL.matcher(dateStr).matches()) { + return LocalDateTime.parse(dateStr, NORMAL_DATE_TIME_FORMATTER); + } else if (NO_SECOND.matcher(dateStr).matches()) { + return LocalDateTime.parse( + dateStr, + NORMAL_DATE_TIME_FORMATTER_NO_SECOND + ); + } else if (NO_MINUTS.matcher(dateStr).matches()) { + return LocalDateTime.parse( + dateStr, + NORMAL_DATE_TIME_FORMATTER_NO_MINUTS + ); + } else if (NO_HOUR.matcher(dateStr).matches()) { + return LocalDateTime.parse( + dateStr + " 00", + NORMAL_DATE_TIME_FORMATTER_NO_MINUTS + ); + } + throw new RuntimeException("时间参数错误: " + dateStr); + } + + public static String getDateStrNow() { + return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockUtil.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockUtil.java new file mode 100644 index 0000000..2592ab2 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockUtil.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock; + +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.lock.suppert.LambdaMeta; +import com.zerolinck.passiflora.common.util.lock.suppert.LambdaUtils; +import com.zerolinck.passiflora.common.util.lock.suppert.SFunction; +import com.zerolinck.passiflora.common.util.lock.suppert.reflect.PropertyNamer; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.function.Supplier; +import lombok.Setter; +import lombok.SneakyThrows; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; + +/** + * 分布式锁工具类,保证在并发情况下的数据正确 + * + * @author linck + * @since 2024-03-20 + */ +public class LockUtil { + + @Setter + private static RedissonClient redissonClient; + + @Setter + private static TransactionTemplate transactionTemplate; + + private static final Map LAMBDA_META_CACHE = + new ConcurrentHashMap<>(); + + private static final Map, Map> FIELD_NAME_CACHE = + new ConcurrentHashMap<>(); + + private static final Map, Map> METHOD_CACHE = + new ConcurrentHashMap<>(); + + public static Object lockAndTransactionalLogic( + String lockKey, + LockWrapper lockWrapper, + Supplier supplier + ) { + AtomicInteger lockLength = new AtomicInteger( + lockWrapper.getColumns().size() + ); + lockWrapper + .getEntityListLock() + .forEach((clazz, list) -> lockLength.addAndGet(list.size())); + RLock[] locks = new RLock[lockLength.get()]; + + try { + AtomicInteger i = new AtomicInteger(); + for ( + ; + i.get() < lockWrapper.getColumns().size(); + i.getAndIncrement() + ) { + String fieldName = getFieldName( + lockWrapper.getColumns().get(i.get()) + ); + locks[i.get()] = + redissonClient.getFairLock( + lockKey + + fieldName + + ":" + + lockWrapper.getColumnValues().get(i.get()) + ); + locks[i.get()].tryLock(10, 60, TimeUnit.SECONDS); + } + lockWrapper + .getEntityListLock() + .forEach((function, entityList) -> { + Method method = getMethod(function); + for (Object entity : entityList) { + String fieldName = getFieldName(function); + Object value; + try { + value = method.invoke(entity); + } catch (Exception e) { + throw new RuntimeException(e); + } + locks[i.get()] = + redissonClient.getFairLock( + lockKey + fieldName + ":" + value + ); + try { + locks[i.get()].tryLock(10, 60, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new BizException( + e, + ResultCodeEnum.COMPETE_FAILED + ); + } + i.getAndIncrement(); + } + }); + return transactionTemplate.execute( + (TransactionCallback) status -> supplier.get() + ); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new BizException(e, ResultCodeEnum.COMPETE_FAILED); + } finally { + unlock(locks); + } + } + + public static void unlock(Lock... locks) { + for (Lock lock : locks) { + if (lock != null) { + lock.unlock(); + } + } + } + + private static String getFieldName(SFunction column) { + if (!LAMBDA_META_CACHE.containsKey(column.toString())) { + LAMBDA_META_CACHE.put( + column.toString(), + LambdaUtils.extract(column) + ); + } + LambdaMeta meta = LAMBDA_META_CACHE.get(column.toString()); + if (!FIELD_NAME_CACHE.containsKey(meta.getInstantiatedClass())) { + FIELD_NAME_CACHE.put(meta.getInstantiatedClass(), new HashMap<>()); + } + if ( + !FIELD_NAME_CACHE + .get(meta.getInstantiatedClass()) + .containsKey(meta.getImplMethodName()) + ) { + String fieldName = PropertyNamer.methodToProperty( + meta.getImplMethodName() + ); + FIELD_NAME_CACHE + .get(meta.getInstantiatedClass()) + .put(meta.getImplMethodName(), fieldName); + } + return FIELD_NAME_CACHE + .get(meta.getInstantiatedClass()) + .get(meta.getImplMethodName()); + } + + @SneakyThrows + private static Method getMethod(SFunction column) { + if (!LAMBDA_META_CACHE.containsKey(column.toString())) { + LAMBDA_META_CACHE.put( + column.toString(), + LambdaUtils.extract(column) + ); + } + LambdaMeta meta = LAMBDA_META_CACHE.get(column.toString()); + Class instantiatedClass = meta.getInstantiatedClass(); + String implMethodName = meta.getImplMethodName(); + if (!METHOD_CACHE.containsKey(instantiatedClass)) { + METHOD_CACHE.put(instantiatedClass, new HashMap<>()); + } + if (!METHOD_CACHE.get(instantiatedClass).containsKey(implMethodName)) { + METHOD_CACHE + .get(instantiatedClass) + .put( + implMethodName, + instantiatedClass.getMethod(implMethodName) + ); + } + return METHOD_CACHE.get(instantiatedClass).get(implMethodName); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockWrapper.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockWrapper.java new file mode 100644 index 0000000..34b3286 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/LockWrapper.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock; + +import cn.hutool.core.collection.CollectionUtil; +import com.zerolinck.passiflora.common.util.lock.suppert.SFunction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +/** + * @author linck + * @since 2024-05-04 + */ +@SuppressWarnings("all") +public class LockWrapper { + + @Getter + private List> columns = new ArrayList<>(); + + @Getter + private List columnValues = new ArrayList<>(); + + @Getter + private Map, List> entityListLock = + new HashMap<>(); + + public LockWrapper lock(SFunction column, String columnValue) { + if (columnValue == null) { + return this; + } + columns.add(column); + columnValues.add(columnValue); + return this; + } + + public LockWrapper lock( + SFunction column, + List entityList + ) { + if (CollectionUtil.isEmpty(entityList)) { + return this; + } + entityListLock.put(column, entityList); + return this; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ClassUtils.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ClassUtils.java new file mode 100644 index 0000000..8648839 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ClassUtils.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import cn.hutool.core.lang.Assert; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; + +/** + * ClassUtils + * + * @author Caratacus + * @author HCL + * @since 2017/07/08 + */ +public final class ClassUtils { + + private static ClassLoader systemClassLoader; + + static { + try { + systemClassLoader = ClassLoader.getSystemClassLoader(); + } catch (SecurityException ignored) { + // AccessControlException on Google App Engine + } + } + + /** 代理 class 的名称 */ + private static final List PROXY_CLASS_NAMES = Arrays.asList( + "net.sf.cglib.proxy.Factory", // cglib + "org.springframework.cglib.proxy.Factory", + "javassist.util.proxy.ProxyObject", // javassist + "org.apache.ibatis.javassist.util.proxy.ProxyObject" + ); + + private ClassUtils() {} + + /** + * 判断传入的类型是否是布尔类型 + * + * @param type 类型 + * @return 如果是原生布尔或者包装类型布尔,均返回 true + */ + public static boolean isBoolean(Class type) { + return type == boolean.class || Boolean.class == type; + } + + /** + * 判断是否为代理对象 + * + * @param clazz 传入 class 对象 + * @return 如果对象class是代理 class,返回 true + */ + public static boolean isProxy(Class clazz) { + if (clazz != null) { + for (Class cls : clazz.getInterfaces()) { + if (PROXY_CLASS_NAMES.contains(cls.getName())) { + return true; + } + } + } + return false; + } + + /** + * 获取当前对象的 class + * + * @param clazz 传入 + * @return 如果是代理的class,返回父 class,否则返回自身 + */ + public static Class getUserClass(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return isProxy(clazz) ? clazz.getSuperclass() : clazz; + } + + /** + * 获取当前对象的class + * + * @param object 对象 + * @return 返回对象的 user class + */ + public static Class getUserClass(Object object) { + Assert.notNull(object, "Instance must not be null"); + return getUserClass(object.getClass()); + } + + /** + * 根据指定的 class , 实例化一个对象,根据构造参数来实例化 + * + *

在 java9 及其之后的版本 Class.newInstance() 方法已被废弃 + * + * @param clazz 需要实例化的对象 + * @param 类型,由输入类型决定 + * @return 返回新的实例 + */ + public static T newInstance(Class clazz) { + try { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch ( + InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException e + ) { + throw new RuntimeException( + String.format( + "实例化对象时出现错误,请尝试给 %s 添加无参的构造方法", + clazz.getName() + ), + e + ); + } + } + + /** + * 实例化对象. + * + * @param clazzName 类名 + * @param 类型 + * @return 实例 + * @since 3.3.2 + */ + @SuppressWarnings("unchecked") + public static T newInstance(String clazzName) { + return (T) newInstance(toClassConfident(clazzName)); + } + + /** + * 请仅在确定类存在的情况下调用该方法 + * + * @param name 类名称 + * @return 返回转换后的 Class + */ + public static Class toClassConfident(String name) { + return toClassConfident(name, null); + } + + /** + * @param name + * @param classLoader + * @return + * @since 3.4.3 + */ + public static Class toClassConfident( + String name, + ClassLoader classLoader + ) { + try { + return loadClass(name, getClassLoaders(classLoader)); + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "找不到指定的class!请仅在明确确定会有 class 的时候,调用该方法", + e + ); + } + } + + private static Class loadClass( + String className, + ClassLoader[] classLoaders + ) throws ClassNotFoundException { + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + try { + return Class.forName(className, true, classLoader); + } catch (ClassNotFoundException e) { + // ignore + } + } + } + throw new ClassNotFoundException("Cannot find class: " + className); + } + + /** + * Determine the name of the package of the given class, e.g. "java.lang" for the {@code + * java.lang.String} class. + * + * @param clazz the class + * @return the package name, or the empty String if the class is defined in the default package + */ + public static String getPackageName(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return getPackageName(clazz.getName()); + } + + /** + * Determine the name of the package of the given fully-qualified class name, e.g. "java.lang" for + * the {@code java.lang.String} class name. + * + * @param fqClassName the fully-qualified class name + * @return the package name, or the empty String if the class is defined in the default package + */ + public static String getPackageName(String fqClassName) { + Assert.notNull(fqClassName, "Class name must not be null"); + int lastDotIndex = fqClassName.lastIndexOf( + com.baomidou.mybatisplus.core.toolkit.StringPool.DOT + ); + return ( + lastDotIndex != -1 + ? fqClassName.substring(0, lastDotIndex) + : StringPool.EMPTY + ); + } + + /** + * Return the default ClassLoader to use: typically the thread context ClassLoader, if available; + * the ClassLoader that loaded the ClassUtils class will be used as fallback. + * + *

Call this method if you intend to use the thread context ClassLoader in a scenario where you + * clearly prefer a non-null ClassLoader reference: for example, for class path resource loading + * (but not necessarily for {@code Class.forName}, which accepts a {@code null} ClassLoader + * reference as well). + * + * @return the default ClassLoader (only {@code null} if even the system ClassLoader isn't + * accessible) + * @see Thread#getContextClassLoader() + * @see ClassLoader#getSystemClassLoader() + * @since 3.3.2 + */ + @Deprecated + public static ClassLoader getDefaultClassLoader() { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (Throwable ex) { + // Cannot access thread context ClassLoader - falling back... + } + if (cl == null) { + // No thread context class loader -> use class loader of this class. + cl = ClassUtils.class.getClassLoader(); + if (cl == null) { + // getClassLoader() returning null indicates the bootstrap ClassLoader + try { + cl = ClassLoader.getSystemClassLoader(); + } catch (Throwable ex) { + // Cannot access system ClassLoader - oh well, maybe the caller can live with + // null... + } + } + } + return cl; + } + + private static ClassLoader[] getClassLoaders(ClassLoader classLoader) { + return new ClassLoader[] { + classLoader, + ClassLoader.getSystemClassLoader(), + Thread.currentThread().getContextClassLoader(), + ClassUtils.class.getClassLoader(), + systemClassLoader, + }; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/CollectionUtils.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/CollectionUtils.java new file mode 100644 index 0000000..4327d9e --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/CollectionUtils.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.util.*; +import java.util.function.Function; + +/** + * Collection工具类 + * + * @author Caratacus + * @since 2016-09-19 + */ +public class CollectionUtils { + + private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + + private static boolean isJdk8; + + static { + // Java 8 + // Java 9+: 9,11,17 + try { + isJdk8 = System.getProperty("java.version").startsWith("1.8."); + } catch (Exception ignore) { + isJdk8 = true; + } + } + + /** + * 校验集合是否为空 + * + * @param coll 入参 + * @return boolean + */ + public static boolean isEmpty(Collection coll) { + return (coll == null || coll.isEmpty()); + } + + /** + * 校验集合是否不为空 + * + * @param coll 入参 + * @return boolean + */ + public static boolean isNotEmpty(Collection coll) { + return !isEmpty(coll); + } + + /** + * 判断Map是否为空 + * + * @param map 入参 + * @return boolean + */ + public static boolean isEmpty(Map map) { + return (map == null || map.isEmpty()); + } + + /** + * 判断Map是否不为空 + * + * @param map 入参 + * @return boolean + */ + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } + + /** + * 创建默认HashMap + * + * @param K + * @param V + * @return HashMap + * @see com.google.common.collect.Maps#newHashMap() + * @since 3.4.0 + */ + public static HashMap newHashMap() { + return new HashMap<>(); + } + + /** + * 根据预期大小创建HashMap. + * + * @param expectedSize 预期大小 + * @param K + * @param V + * @return HashMap + * @see com.google.common.collect.Maps#newHashMapWithExpectedSize + * @since 3.4.0 + */ + public static HashMap newHashMapWithExpectedSize( + int expectedSize + ) { + return new HashMap<>(capacity(expectedSize)); + } + + /** + * 用来过渡下Jdk1.8下ConcurrentHashMap的性能bug https://bugs.openjdk.java.net/browse/JDK-8161372 + * + *

A temporary workaround for Java 8 ConcurrentHashMap#computeIfAbsent specific performance + * issue: JDK-8161372.
+ * + * @see https://bugs.openjdk.java.net/browse/JDK-8161372 + * @param concurrentHashMap ConcurrentHashMap 没限制类型了,非ConcurrentHashMap就别调用这方法了 + * @param key key + * @param mappingFunction function + * @param k + * @param v + * @return V + * @since 3.4.0 + */ + public static V computeIfAbsent( + Map concurrentHashMap, + K key, + Function mappingFunction + ) { + Objects.requireNonNull(mappingFunction); + if (isJdk8) { + V v = concurrentHashMap.get(key); + if (null == v) { + // issue#11986 lock bug + // v = map.computeIfAbsent(key, func); + + // this bug fix methods maybe cause `func.apply` multiple calls. + v = mappingFunction.apply(key); + if (null == v) { + return null; + } + final V res = concurrentHashMap.putIfAbsent(key, v); + if (null != res) { + // if pre value present, means other thread put value already, and putIfAbsent + // not effect + // return exist value + return res; + } + // if pre value is null, means putIfAbsent effected, return current value + } + return v; + } else { + return concurrentHashMap.computeIfAbsent(key, mappingFunction); + } + } + + /** + * Returns a capacity that is sufficient to keep the map from being resized as long as it grows no + * larger than expectedSize and the load factor is >= its default (0.75). + * + * @see com.google.common.collect.Maps#capacity(int) + * @since 3.4.0 + */ + private static int capacity(int expectedSize) { + if (expectedSize < 3) { + if (expectedSize < 0) { + throw new IllegalArgumentException( + "expectedSize cannot be negative but was: " + expectedSize + ); + } + return expectedSize + 1; + } + if (expectedSize < MAX_POWER_OF_TWO) { + // This is the calculation used in JDK8 to resize when a putAll + // happens; it seems to be the most conservative calculation we + // can make. 0.75 is the default load factor. + return (int) ((float) expectedSize / 0.75F + 1.0F); + } + return Integer.MAX_VALUE; // any large value + } + + // 提供处理Map多key取值工具方法 + + /** + * 批量取出Map中的值 + * + * @param map map + * @param keys 键的集合 + * @param key的泛型 + * @param value的泛型 + * @return value的泛型的集合 + */ + public static List getCollection( + Map map, + Iterable keys + ) { + List result = new ArrayList<>(); + if (map != null && !map.isEmpty() && keys != null) { + keys.forEach(key -> + Optional.ofNullable(map.get(key)).ifPresent(result::add) + ); + } + return result; + } + + /** + * 批量取出Map中的值 + * + * @param map map + * @param keys 键的集合 + * @param comparator 排序器 + * @param key的泛型 + * @param value的泛型 + * @return value的泛型的集合 + */ + public static List getCollection( + Map map, + Iterable keys, + Comparator comparator + ) { + Objects.requireNonNull(comparator); + List result = getCollection(map, keys); + Collections.sort(result, comparator); + return result; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/GenericTypeUtils.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/GenericTypeUtils.java new file mode 100644 index 0000000..b4565c5 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/GenericTypeUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import com.zerolinck.passiflora.common.util.lock.suppert.reflect.IGenericTypeResolver; +import com.zerolinck.passiflora.common.util.lock.suppert.reflect.SpringReflectionHelper; + +/** + * 泛型类工具(用于隔离Spring的代码) + * + * @author noear + * @author hubin + * @since 2021-09-03 + */ +public class GenericTypeUtils { + + private static IGenericTypeResolver GENERIC_TYPE_RESOLVER; + + /** 获取泛型工具助手 */ + public static Class[] resolveTypeArguments( + final Class clazz, + final Class genericIfc + ) { + if (null == GENERIC_TYPE_RESOLVER) { + // 直接使用 spring 静态方法,减少对象创建 + return SpringReflectionHelper.resolveTypeArguments( + clazz, + genericIfc + ); + } + return GENERIC_TYPE_RESOLVER.resolveTypeArguments(clazz, genericIfc); + } + + /** 设置泛型工具助手。如果不想使用Spring封装,可以使用前替换掉 */ + public static void setGenericTypeResolver( + IGenericTypeResolver genericTypeResolver + ) { + GENERIC_TYPE_RESOLVER = genericTypeResolver; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/IdeaProxyLambdaMeta.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/IdeaProxyLambdaMeta.java new file mode 100644 index 0000000..5e7665f --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/IdeaProxyLambdaMeta.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +/** 在 IDEA 的 Evaluate 中执行的 Lambda 表达式元数据需要使用该类处理元数据 */ +public class IdeaProxyLambdaMeta implements LambdaMeta { + + private final Class clazz; + private final String name; + + public IdeaProxyLambdaMeta(Proxy func) { + InvocationHandler handler = Proxy.getInvocationHandler(func); + try { + Field field = handler.getClass().getDeclaredField("val$target"); + field.setAccessible(true); + MethodHandle dmh = (MethodHandle) field.get(handler); + Executable executable = MethodHandles.reflectAs( + Executable.class, + dmh + ); + clazz = executable.getDeclaringClass(); + name = executable.getName(); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getImplMethodName() { + return name; + } + + @Override + public Class getInstantiatedClass() { + return clazz; + } + + @Override + public String toString() { + return clazz.getSimpleName() + "::" + name; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaMeta.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaMeta.java new file mode 100644 index 0000000..ce18128 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaMeta.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +/** Lambda 信息 */ +public interface LambdaMeta { + /** + * 获取 lambda 表达式实现方法的名称 + * + * @return lambda 表达式对应的实现方法名称 + */ + String getImplMethodName(); + + /** + * 实例化该方法的类 + * + * @return 返回对应的类名称 + */ + Class getInstantiatedClass(); +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaUtils.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaUtils.java new file mode 100644 index 0000000..b03ff00 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/LambdaUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** Lambda 解析工具类 */ +public final class LambdaUtils { + + /** + * 该缓存可能会在任意不定的时间被清除 + * + * @param func 需要解析的 lambda 对象 + * @param 类型,被调用的 Function 对象的目标类型 + * @return 返回解析后的结果 + */ + public static LambdaMeta extract(SFunction func) { + // 1. IDEA 调试模式下 lambda 表达式是一个代理 + if (func instanceof Proxy) { + return new IdeaProxyLambdaMeta((Proxy) func); + } + // 2. 反射读取 + try { + Method method = func.getClass().getDeclaredMethod("writeReplace"); + method.setAccessible(true); + return new ReflectLambdaMeta( + (SerializedLambda) method.invoke(func), + func.getClass().getClassLoader() + ); + } catch (Throwable e) { + // 3. 反射失败使用序列化的方式读取 + return new ShadowLambdaMeta( + com.zerolinck.passiflora.common.util.lock.suppert.SerializedLambda.extract( + func + ) + ); + } + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectLambdaMeta.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectLambdaMeta.java new file mode 100644 index 0000000..c4cac2b --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectLambdaMeta.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.lang.invoke.SerializedLambda; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ReflectLambdaMeta implements LambdaMeta { + + private final SerializedLambda lambda; + + private final ClassLoader classLoader; + + public ReflectLambdaMeta(SerializedLambda lambda, ClassLoader classLoader) { + this.lambda = lambda; + this.classLoader = classLoader; + } + + @Override + public String getImplMethodName() { + return lambda.getImplMethodName(); + } + + @Override + public Class getInstantiatedClass() { + String instantiatedMethodType = lambda.getInstantiatedMethodType(); + String instantiatedType = instantiatedMethodType + .substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)) + .replace(StringPool.SLASH, StringPool.DOT); + return ClassUtils.toClassConfident(instantiatedType, this.classLoader); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectionKit.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectionKit.java new file mode 100644 index 0000000..afcc7d3 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ReflectionKit.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +import cn.hutool.core.lang.Assert; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** 反射工具类,提供反射相关的快捷操作 */ +public final class ReflectionKit { + + /** class field cache */ + private static final Map, List> CLASS_FIELD_CACHE = + new ConcurrentHashMap<>(); + + @Deprecated + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = + new IdentityHashMap<>(8); + + private static final Map, Class> PRIMITIVE_TYPE_TO_WRAPPER_MAP = + new IdentityHashMap<>(8); + + static { + PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); + for (Map.Entry< + Class, + Class + > entry : PRIMITIVE_WRAPPER_TYPE_MAP.entrySet()) { + PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(entry.getValue(), entry.getKey()); + } + } + + /** + * 获取字段值 + * + * @param entity 实体 + * @param fieldName 字段名称 + * @return 属性值 + */ + public static Object getFieldValue(Object entity, String fieldName) { + Class cls = entity.getClass(); + Map fieldMaps = getFieldMap(cls); + try { + Field field = fieldMaps.get(fieldName); + Assert.notNull( + field, + "Error: NoSuchField in %s for %s. Cause:", + cls.getSimpleName(), + fieldName + ); + field.setAccessible(true); + return field.get(entity); + } catch (ReflectiveOperationException e) { + throw new RuntimeException( + String.format( + "Error: Cannot read field in %s. Cause:", + cls.getSimpleName() + ), + e + ); + } + } + + /** + * 反射对象获取泛型 + * + * @param clazz 对象 + * @param genericIfc 所属泛型父类 + * @param index 泛型所在位置 + * @return Class + */ + public static Class getSuperClassGenericType( + final Class clazz, + final Class genericIfc, + final int index + ) { + // update by noear @2021-09-03 + Class[] typeArguments = GenericTypeUtils.resolveTypeArguments( + ClassUtils.getUserClass(clazz), + genericIfc + ); + return null == typeArguments ? null : typeArguments[index]; + } + + /** + * 获取该类的所有属性列表 + * + * @param clazz 反射类 + */ + public static Map getFieldMap(Class clazz) { + List fieldList = getFieldList(clazz); + return CollectionUtils.isNotEmpty(fieldList) + ? fieldList + .stream() + .collect(Collectors.toMap(Field::getName, Function.identity())) + : Collections.emptyMap(); + } + + /** + * 获取该类的所有属性列表 + * + * @param clazz 反射类 + */ + public static List getFieldList(Class clazz) { + if (Objects.isNull(clazz)) { + return Collections.emptyList(); + } + return CollectionUtils.computeIfAbsent( + CLASS_FIELD_CACHE, + clazz, + k -> { + Field[] fields = k.getDeclaredFields(); + List superFields = new ArrayList<>(); + Class currentClass = k.getSuperclass(); + while (currentClass != null) { + Field[] declaredFields = currentClass.getDeclaredFields(); + Collections.addAll(superFields, declaredFields); + currentClass = currentClass.getSuperclass(); + } + /* 排除重载属性 */ + Map fieldMap = excludeOverrideSuperField( + fields, + superFields + ); + /* + * 重写父类属性过滤后处理忽略部分,支持过滤父类属性功能 + * 场景:中间表不需要记录创建时间,忽略父类 createTime 公共属性 + * 中间表实体重写父类属性 ` private transient Date createTime; ` + */ + return fieldMap + .values() + .stream() + /* 过滤静态属性 */ + .filter(f -> !Modifier.isStatic(f.getModifiers())) + /* 过滤 transient关键字修饰的属性 */ + .filter(f -> !Modifier.isTransient(f.getModifiers())) + .collect(Collectors.toList()); + } + ); + } + + /** + * 排序重置父类属性 + * + * @param fields 子类属性 + * @param superFieldList 父类属性 + */ + public static Map excludeOverrideSuperField( + Field[] fields, + List superFieldList + ) { + // 子类属性 + Map fieldMap = Stream + .of(fields) + .collect( + toMap( + Field::getName, + identity(), + (u, v) -> { + throw new IllegalStateException( + String.format("Duplicate key %s", u) + ); + }, + LinkedHashMap::new + ) + ); + superFieldList + .stream() + .filter(field -> !fieldMap.containsKey(field.getName())) + .forEach(f -> fieldMap.put(f.getName(), f)); + return fieldMap; + } + + /** + * 判断是否为基本类型或基本包装类型 + * + * @param clazz class + * @return 是否基本类型或基本包装类型 + */ + @Deprecated + public static boolean isPrimitiveOrWrapper(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return ( + clazz.isPrimitive() || PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(clazz) + ); + } + + public static Class resolvePrimitiveIfNecessary(Class clazz) { + return ( + clazz.isPrimitive() && clazz != void.class + ? PRIMITIVE_TYPE_TO_WRAPPER_MAP.get(clazz) + : clazz + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SFunction.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SFunction.java new file mode 100644 index 0000000..ce2b241 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SFunction.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.io.Serializable; +import java.util.function.Function; + +/** 支持序列化的 Function */ +@FunctionalInterface +public interface SFunction extends Function, Serializable {} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SerializedLambda.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SerializedLambda.java new file mode 100644 index 0000000..5e71595 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/SerializedLambda.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +import java.io.*; + +/** 当前类是 {@link java.lang.invoke.SerializedLambda } 的一个镜像 */ +@SuppressWarnings("ALL") +public class SerializedLambda implements Serializable { + + private static final long serialVersionUID = 8025925345765570181L; + + private Class capturingClass; + private String functionalInterfaceClass; + private String functionalInterfaceMethodName; + private String functionalInterfaceMethodSignature; + private String implClass; + private String implMethodName; + private String implMethodSignature; + private int implMethodKind; + private String instantiatedMethodType; + private Object[] capturedArgs; + + public static SerializedLambda extract(Serializable serializable) { + try ( + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos) + ) { + oos.writeObject(serializable); + oos.flush(); + try ( + ObjectInputStream ois = new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray()) + ) { + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + Class clazz = super.resolveClass(desc); + return clazz == java.lang.invoke.SerializedLambda.class + ? SerializedLambda.class + : clazz; + } + } + ) { + return (SerializedLambda) ois.readObject(); + } + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public String getInstantiatedMethodType() { + return instantiatedMethodType; + } + + public Class getCapturingClass() { + return capturingClass; + } + + public String getImplMethodName() { + return implMethodName; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ShadowLambdaMeta.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ShadowLambdaMeta.java new file mode 100644 index 0000000..87ab56b --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/ShadowLambdaMeta.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +/** 基于 {@link SerializedLambda} 创建的元信息 */ +public class ShadowLambdaMeta implements LambdaMeta { + + private final SerializedLambda lambda; + + public ShadowLambdaMeta(SerializedLambda lambda) { + this.lambda = lambda; + } + + @Override + public String getImplMethodName() { + return lambda.getImplMethodName(); + } + + @Override + public Class getInstantiatedClass() { + String instantiatedMethodType = lambda.getInstantiatedMethodType(); + String instantiatedType = instantiatedMethodType + .substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)) + .replace(StringPool.SLASH, StringPool.DOT); + return ClassUtils.toClassConfident( + instantiatedType, + lambda.getCapturingClass().getClassLoader() + ); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/StringPool.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/StringPool.java new file mode 100644 index 0000000..dc53252 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/StringPool.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert; + +/** + * Copy to jodd.util + * + *

Pool of String constants to prevent repeating of hard-coded String + * literals in the code. Due to fact that these are public static final they will be + * inlined by java compiler and reference to this class will be dropped. There is no + * performance gain of using this pool. Read: + * https://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5 + * + *

    + *
  • Literal strings within the same class in the same package represent references to the same + * String object. + *
  • Literal strings within different classes in the same package represent references to the + * same String object. + *
  • Literal strings within different classes in different packages likewise represent + * references to the same String object. + *
  • Strings computed by constant expressions are computed at compile time and then treated as + * if they were literals. + *
  • Strings computed by concatenation at run time are newly created and therefore distinct. + *
+ */ +public interface StringPool { + String AMPERSAND = "&"; + String AND = "and"; + String AT = "@"; + String ASTERISK = "*"; + String STAR = ASTERISK; + String BACK_SLASH = "\\"; + String COLON = ":"; + String COMMA = ","; + String DASH = "-"; + String DOLLAR = "$"; + String DOT = "."; + String DOTDOT = ".."; + String DOT_CLASS = ".class"; + String DOT_JAVA = ".java"; + String DOT_XML = ".xml"; + String EMPTY = ""; + String EQUALS = "="; + String FALSE = "false"; + String SLASH = "/"; + String HASH = "#"; + String HAT = "^"; + String LEFT_BRACE = "{"; + String LEFT_BRACKET = "("; + String LEFT_CHEV = "<"; + String DOT_NEWLINE = ",\n"; + String NEWLINE = "\n"; + String N = "n"; + String NO = "no"; + String NULL = "null"; + String NUM = "NUM"; + String OFF = "off"; + String ON = "on"; + String PERCENT = "%"; + String PIPE = "|"; + String PLUS = "+"; + String QUESTION_MARK = "?"; + String EXCLAMATION_MARK = "!"; + String QUOTE = "\""; + String RETURN = "\r"; + String TAB = "\t"; + String RIGHT_BRACE = "}"; + String RIGHT_BRACKET = ")"; + String RIGHT_CHEV = ">"; + String SEMICOLON = ";"; + String SINGLE_QUOTE = "'"; + String BACKTICK = "`"; + String SPACE = " "; + String SQL = "sql"; + String TILDA = "~"; + String LEFT_SQ_BRACKET = "["; + String RIGHT_SQ_BRACKET = "]"; + String TRUE = "true"; + String UNDERSCORE = "_"; + String UTF_8 = "UTF-8"; + String US_ASCII = "US-ASCII"; + String ISO_8859_1 = "ISO-8859-1"; + String Y = "y"; + String YES = "yes"; + String ONE = "1"; + String ZERO = "0"; + String DOLLAR_LEFT_BRACE = "${"; + String HASH_LEFT_BRACE = "#{"; + String CRLF = "\r\n"; + + String HTML_NBSP = " "; + String HTML_AMP = "&"; + String HTML_QUOTE = """; + String HTML_LT = "<"; + String HTML_GT = ">"; + + // ---------------------------------------------------------------- array + + String[] EMPTY_ARRAY = new String[0]; + + byte[] BYTES_NEW_LINE = StringPool.NEWLINE.getBytes(); +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/IGenericTypeResolver.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/IGenericTypeResolver.java new file mode 100644 index 0000000..091a6ed --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/IGenericTypeResolver.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert.reflect; + +/** + * 泛型类助手(用于隔离Spring的代码) + * + * @author noear + * @author hubin + * @since 2021-09-03 + */ +public interface IGenericTypeResolver { + Class[] resolveTypeArguments( + final Class clazz, + final Class genericIfc + ); +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/PropertyNamer.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/PropertyNamer.java new file mode 100644 index 0000000..7a65da6 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/PropertyNamer.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert.reflect; + +import java.util.Locale; + +public final class PropertyNamer { + + private PropertyNamer() { + // Prevent Instantiation of Static Class + } + + public static String methodToProperty(String name) { + if (name.startsWith("is")) { + name = name.substring(2); + } else if (name.startsWith("get") || name.startsWith("set")) { + name = name.substring(3); + } else { + throw new RuntimeException( + "Error parsing property name '" + + name + + "'. Didn't start with 'is', 'get' or 'set'." + ); + } + + if ( + name.length() == 1 || + (name.length() > 1 && !Character.isUpperCase(name.charAt(1))) + ) { + name = + name.substring(0, 1).toLowerCase(Locale.ENGLISH) + + name.substring(1); + } + + return name; + } + + public static boolean isProperty(String name) { + return isGetter(name) || isSetter(name); + } + + public static boolean isGetter(String name) { + return ( + (name.startsWith("get") && name.length() > 3) || + (name.startsWith("is") && name.length() > 2) + ); + } + + public static boolean isSetter(String name) { + return name.startsWith("set") && name.length() > 3; + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/SpringReflectionHelper.java b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/SpringReflectionHelper.java new file mode 100644 index 0000000..6a82a2f --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/java/com/zerolinck/passiflora/common/util/lock/suppert/reflect/SpringReflectionHelper.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.common.util.lock.suppert.reflect; + +import org.springframework.core.GenericTypeResolver; + +/** + * Spring 反射辅助类 + * + * @author noear + * @author hubin + * @since 2021-09-03 + */ +public class SpringReflectionHelper { + + public static Class[] resolveTypeArguments( + Class clazz, + Class genericIfc + ) { + return GenericTypeResolver.resolveTypeArguments(clazz, genericIfc); + } +} diff --git a/passiflora-server/modules/passiflora-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/passiflora-server/modules/passiflora-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..8cc2cd7 --- /dev/null +++ b/passiflora-server/modules/passiflora-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,7 @@ +com.zerolinck.passiflora.common.config.jackson.JacksonConfig +com.zerolinck.passiflora.common.config.mybaits.MybatisConfig +com.zerolinck.passiflora.common.handler.GlobalExceptionHandler +com.zerolinck.passiflora.common.util.SpringContextHolder +com.zerolinck.passiflora.common.config.GlobalFilter +com.zerolinck.passiflora.common.config.LockConfig +com.zerolinck.passiflora.common.config.CacheConfig \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-feign/build.gradle b/passiflora-server/modules/passiflora-feign/build.gradle new file mode 100644 index 0000000..f4bf0f0 --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java-library' +} + +configurations { + all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' +} + +dependencies { + api project(':modules:passiflora-common') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + api 'org.springframework.cloud:spring-cloud-openfeign-core' + api 'io.swagger.core.v3:swagger-annotations-jakarta' + api 'cn.hutool:hutool-extra' + api 'io.github.openfeign:feign-core' + implementation 'org.apache.tomcat.embed:tomcat-embed-core' +} \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/config/FeignConfiguration.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/config/FeignConfiguration.java new file mode 100644 index 0000000..17a7598 --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/config/FeignConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.config; + +import cn.hutool.extra.spring.SpringUtil; +import com.zerolinck.passiflora.common.util.NetUtil; +import com.zerolinck.passiflora.model.common.constant.Constants; +import feign.Logger; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; + +/** + * @author linck + * @since 2023-12-15 + */ +@Slf4j +public class FeignConfiguration implements RequestInterceptor { + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.HEADERS; + } + + @Override + public void apply(RequestTemplate requestTemplate) { + HttpServletRequest request = NetUtil.getRequest(); + String authorization = request.getHeader("authorization"); + String traceId = request.getHeader("traceId"); + requestTemplate.header(Constants.Authorization, authorization); + requestTemplate.header(Constants.traceId, traceId); + requestTemplate.header( + "req-from", + SpringUtil.getProperty("spring.application.name") + ); + } +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/storage/StorageFileApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/storage/StorageFileApi.java new file mode 100644 index 0000000..fab7454 --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/storage/StorageFileApi.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.storage; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author linck + * @since 2024-05-17 + */ +@Tag(name = "通用文件") +@FeignClient( + value = "storageFile", + contextId = "storageFile", + path = "/passiflora/storage-api/storageFile", + configuration = FeignConfiguration.class +) +public interface StorageFileApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page( + QueryCondition condition + ); + + @Operation(summary = "根据文件ids获取列表") + @PostMapping("listByFileIds") + Result> listByFileIds(@RequestBody List fileIds); + + /** + * @param storageFile 需要参数 originalFileName,contentType, fileMd5 + * @return 空字符串表示无法秒传,应再次调用文件上传接口;有值字符串表示上传成功,上传文件ID + */ + @Operation( + summary = "尝试文件秒传", + description = "需要参数 originalFileName,contentType, fileMd5。\n" + + "返回空字符串表示无法秒传,应再次调用文件上传接口;有值字符串表示上传成功,上传文件ID" + ) + @PostMapping("tryQuicklyUpload") + Result tryQuicklyUpload(@RequestBody StorageFile storageFile); + + @Operation(summary = "文件上传") + @PostMapping("upload") + Result upload( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "fileName", required = false) String fileName + ); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail(@RequestParam(value = "fileId") String fileId); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List fileIds); + + @Operation(summary = "文件下载") + @GetMapping(value = "/downloadFile") + void downloadFile(@RequestParam("fileId") String fileId); + + @Operation(summary = "文件批量下载") + @PostMapping(value = "/downloadZip") + void downloadZip(@RequestBody List fileIds); + + @Operation( + summary = "确认文件使用", + description = "文件由临时文件转换为正式文件" + ) + @PostMapping(value = "/confirmFile") + Result confirmFile(@RequestBody List fileIds); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/EnumApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/EnumApi.java new file mode 100644 index 0000000..6e0536f --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/EnumApi.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import java.util.Map; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +/** + * @author linck + * @since 2023-12-13 + */ +@FeignClient( + value = "enum", + contextId = "enum", + path = "/passiflora/system-api/enum", + configuration = FeignConfiguration.class +) +@Tag(name = "枚举查询") +public interface EnumApi { + @Operation(summary = "查询") + @GetMapping("/{enumName}") + Result>> get( + @PathVariable("enumName") String enumName + ); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictApi.java new file mode 100644 index 0000000..7185ebe --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictApi.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author linck + * @since 2024-04-01 + */ +@Tag(name = "字典") +@FeignClient( + value = "sysDict", + contextId = "sysDict", + path = "/passiflora/system-api/sysDict", + configuration = FeignConfiguration.class +) +public interface SysDictApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page(QueryCondition condition); + + @Operation(summary = "新增") + @PostMapping("add") + Result add(@RequestBody @Validated(Insert.class) SysDict sysDict); + + @Operation(summary = "更新") + @PostMapping("update") + Result update( + @RequestBody @Validated(Update.class) SysDict sysDict + ); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail(@RequestParam(value = "dictId") String dictId); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List dictIds); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictItemApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictItemApi.java new file mode 100644 index 0000000..b92da1d --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysDictItemApi.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysDictItem; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author linck + * @since 2024-04-01 + */ +@Tag(name = "字典项") +@FeignClient( + value = "sysDictItem", + contextId = "sysDictItem", + path = "/passiflora/system-api/sysDictItem", + configuration = FeignConfiguration.class +) +public interface SysDictItemApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page( + QueryCondition condition + ); + + @Operation(summary = "新增") + @PostMapping("add") + Result add( + @RequestBody @Validated(Insert.class) SysDictItem sysDictItem + ); + + @Operation(summary = "更新") + @PostMapping("update") + Result update( + @RequestBody @Validated(Update.class) SysDictItem sysDictItem + ); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail( + @RequestParam(value = "dictItemId") String dictItemId + ); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List dictItemIds); + + @Operation(summary = "根据字典ID查询") + @GetMapping("listByDictId") + Result> listByDictId( + @RequestParam(value = "dictId") String dictId + ); + + @Operation(summary = "根据字典名称查询") + @GetMapping("listByDictName") + Result> listByDictName( + @RequestParam(value = "dictName") String dictName + ); + + @Operation(summary = "根据字典标识查询") + @GetMapping("listByDictTag") + Result> listByDictTag( + @RequestParam(value = "dictTag") String dictTag + ); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysOrgApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysOrgApi.java new file mode 100644 index 0000000..bbb93fa --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysOrgApi.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import com.zerolinck.passiflora.model.system.vo.SysOrgVo; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author linck + * @since 2024-04-09 + */ +@Tag(name = "机构") +@FeignClient( + value = "sysOrg", + contextId = "sysOrg", + path = "/passiflora/system-api/sysOrg", + configuration = FeignConfiguration.class +) +public interface SysOrgApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page(QueryCondition condition); + + @Operation(summary = "新增") + @PostMapping("add") + Result add(@RequestBody @Validated(Insert.class) SysOrg sysOrg); + + @Operation(summary = "更新") + @PostMapping("update") + Result update(@RequestBody @Validated(Update.class) SysOrg sysOrg); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail(@RequestParam(value = "orgId") String orgId); + + /** 此方法会级联删除下级机构 */ + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List orgIds); + + @Operation(summary = "根据父级ID查询子机构列表") + @GetMapping("listByParentId") + Result> listByParentId( + @RequestParam( + required = false, + value = "orgParentId" + ) String orgParentId + ); + + @Operation(summary = "机构树") + @GetMapping("orgTree") + Result> orgTree(); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPermissionApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPermissionApi.java new file mode 100644 index 0000000..aa65263 --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPermissionApi.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import com.zerolinck.passiflora.model.system.vo.SysPermissionVo; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author linck + * @since 2024-05-06 + */ +@Tag(name = "菜单") +@FeignClient( + value = "sysPermission", + contextId = "sysPermission", + path = "/passiflora/system-api/sysPermission", + configuration = FeignConfiguration.class +) +public interface SysPermissionApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page( + QueryCondition condition + ); + + @Operation(summary = "新增") + @PostMapping("add") + Result add( + @RequestBody @Validated(Insert.class) SysPermission sysPermission + ); + + @Operation(summary = "更新") + @PostMapping("update") + Result update( + @RequestBody @Validated(Update.class) SysPermission sysPermission + ); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail( + @RequestParam(value = "permissionId") String permissionId + ); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List permissionIds); + + @Operation(summary = "菜单树") + @GetMapping("menuTree") + Result> menuTree(); + + @Operation(summary = "权限树-列表使用") + @GetMapping("permissionTableTree") + Result> permissionTableTree(); + + @Operation(summary = "更新排序") + @PostMapping("updateOrder") + Result updateOrder( + @RequestBody @Validated(Update.class) List< + SysPermissionTableVo + > sysPermissionTableVos + ); + + @Operation(summary = "禁用") + @PostMapping("disable") + Result disable(@RequestBody List permissionIds); + + @Operation(summary = "启用") + @PostMapping("enable") + Result enable(@RequestBody List permissionIds); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPositionApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPositionApi.java new file mode 100644 index 0000000..268dc1c --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysPositionApi.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import com.zerolinck.passiflora.model.system.vo.SysPositionVo; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author linck + * @since 2024-05-14 + */ +@Tag(name = "职位") +@FeignClient( + value = "sysPosition", + contextId = "sysPosition", + path = "/passiflora/system-api/sysPosition", + configuration = FeignConfiguration.class +) +public interface SysPositionApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page( + QueryCondition condition + ); + + @Operation(summary = "新增") + @PostMapping("add") + Result add( + @RequestBody @Validated(Insert.class) SysPosition sysPosition + ); + + @Operation(summary = "更新") + @PostMapping("update") + Result update( + @RequestBody @Validated(Update.class) SysPosition sysPosition + ); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail( + @RequestParam(value = "positionId") String positionId + ); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(@RequestBody List positionIds); + + @Operation(summary = "职位树") + @GetMapping("positionTree") + Result> positionTree(); + + @Operation(summary = "禁用") + @PostMapping("disable") + Result disable(@RequestBody List positionIds); + + @Operation(summary = "启用") + @PostMapping("enable") + Result enable(@RequestBody List positionIds); + + @Operation(summary = "更新排序") + @PostMapping("updateOrder") + Result updateOrder( + @RequestBody @Validated(Update.class) List sysPositionVos + ); +} diff --git a/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysUserApi.java b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysUserApi.java new file mode 100644 index 0000000..072b764 --- /dev/null +++ b/passiflora-server/modules/passiflora-feign/src/main/java/com/zerolinck/passiflora/feign/system/SysUserApi.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.feign.system; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.config.FeignConfiguration; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import com.zerolinck.passiflora.model.system.vo.SysUserVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author linck + * @since 2024-03-19 + */ +@Tag(name = "用户") +@FeignClient( + value = "sysUser", + contextId = "sysUser", + path = "/passiflora/system-api/sysUser", + configuration = FeignConfiguration.class +) +public interface SysUserApi { + @Operation(summary = "分页查询") + @GetMapping("page") + Result> page( + @RequestParam(value = "orgId", required = false) String orgId, + QueryCondition condition + ); + + @Operation(summary = "新增") + @PostMapping("add") + Result add(SysUser sysUser); + + @Operation(summary = "更新") + @PostMapping("update") + Result update(SysUser sysUser); + + @Operation(summary = "详情") + @GetMapping("detail") + Result detail(@RequestParam(value = "userId") String userId); + + @Operation(summary = "删除") + @PostMapping("delete") + Result delete(List userIds); + + @Operation( + summary = "登录", + description = "需要参数:userName,userPassword" + ) + @PostMapping("login") + Result login(SysUser sysUser); + + @Operation(summary = "退出登录") + @GetMapping("logout") + Result logout(); + + @Operation(summary = "获取当前登录用户信息") + @GetMapping("currentUserInfo") + Result currentUserInfo(); + + /** + * 网关校验 Token 有效性调用 + * @return true-有效,false-无效 + */ + @Operation(summary = "验证token") + @GetMapping("checkToken") + Result checkToken(); +} diff --git a/passiflora-server/modules/passiflora-model/build.gradle b/passiflora-server/modules/passiflora-model/build.gradle new file mode 100644 index 0000000..caefdcc --- /dev/null +++ b/passiflora-server/modules/passiflora-model/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' + id 'java-library' +} + +dependencies { + api platform(project(':modules:passiflora-bom')) + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + compileOnlyApi 'org.projectlombok:lombok' + compileOnly 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.20' + compileOnly 'jakarta.validation:jakarta.validation-api' + compileOnly 'org.hibernate.validator:hibernate-validator' + compileOnly 'com.baomidou:mybatis-plus-annotation' + api 'org.mybatis:mybatis' + api 'org.mapstruct:mapstruct' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/BaseEntity.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/BaseEntity.java new file mode 100644 index 0000000..498e40e --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/BaseEntity.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.Version; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.Data; + +/** + * @author linck + * @since 2024-02-06 + */ +@Data +public class BaseEntity { + + @TableField(fill = FieldFill.INSERT) + @Schema(description = "创建用户id", maxLength = 20, hidden = true) + private String createBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + @Schema(description = "更新用户id", maxLength = 20, hidden = true) + private String updateBy; + + @TableField(fill = FieldFill.INSERT) + @Schema(description = "创建时间", hidden = true) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + @Schema(description = "更新时间", hidden = true) + private LocalDateTime updateTime; + + @TableLogic + @Schema(description = "删除标识", hidden = true) + private Integer delFlag; + + @Version + @Schema(description = "乐观锁版本", hidden = true) + @NotNull(groups = { Update.class }, message = "version不能为空") private Long version; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/LabelValueInterface.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/LabelValueInterface.java new file mode 100644 index 0000000..76d0670 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/LabelValueInterface.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common; + +/** + * @author linck + * @since 2024-02-06 + */ +public interface LabelValueInterface { + String getLabel(); + + Integer getValue(); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/Constants.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/Constants.java new file mode 100644 index 0000000..d39d4bb --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/Constants.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common.constant; + +/** + * @author linck + */ +public interface Constants { + String Authorization = "Authorization"; + + String traceId = "traceId"; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/RedisPrefix.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/RedisPrefix.java new file mode 100644 index 0000000..dfcfec4 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/constant/RedisPrefix.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common.constant; + +/** + * @author linck + * @since 2024-04-01 + */ +public interface RedisPrefix { + String TOKEN_KEY = "passiflora:token:"; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/StatusEnum.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/StatusEnum.java new file mode 100644 index 0000000..1f80012 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/StatusEnum.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common.enums; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author linck + * @since 2024-05-08 + */ +@Getter +@AllArgsConstructor +public enum StatusEnum implements LabelValueInterface { + DISABLE("禁用", 0), + ENABLE("启用", 1); + + private final String label; + + private final Integer value; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/YesOrNoEnum.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/YesOrNoEnum.java new file mode 100644 index 0000000..f830217 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/common/enums/YesOrNoEnum.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.common.enums; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author linck + * @since 2024-04-02 + */ +@Getter +@AllArgsConstructor +public enum YesOrNoEnum implements LabelValueInterface { + NO("否", 0), + YES("是", 1); + + private final String label; + + private final Integer value; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/entity/StorageFile.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/entity/StorageFile.java new file mode 100644 index 0000000..6ca6597 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/entity/StorageFile.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.storage.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.storage.enums.FileStatusEnum; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * 通用文件 Entity + * + * @author linck + * @since 2024-05-17 + */ +@Data +@Schema(description = "通用文件") +@EqualsAndHashCode(callSuper = false) +public class StorageFile extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "文件ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "文件ID长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "文件ID不能为空") + private String fileId; + + @Schema(description = "文件名称", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "文件名称长度不能大于100" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "文件名称不能为空" + ) + private String originalFileName; + + @Schema(description = "文件用途", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "文件用途长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "文件用途不能为空" + ) + private String filePurpose; + + @Schema(description = "储存桶名称", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "储存桶名称长度不能大于50" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "储存桶名称不能为空" + ) + private String bucketName; + + @Schema(description = "储存对象名称", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "储存对象名称长度不能大于50" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "储存对象名称不能为空" + ) + private String objectName; + + @Schema(description = "文件大小") + @NotNull( + groups = { Insert.class, Update.class }, + message = "文件大小不能为空" + ) + private Long fileSize; + + @Schema(description = "文件contentType") + @NotBlank( + groups = { Insert.class, Update.class }, + message = "文件contentType不能为空" + ) + private String contentType; + + @Schema(description = "文件MD5", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "文件MD5长度不能大于50" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "文件MD5不能为空" + ) + private String fileMd5; + + @Schema(description = "文件下载次数") + @NotNull( + groups = { Insert.class, Update.class }, + message = "文件下载次数不能为空" + ) + private Long downloadCount; + + @Schema(description = "最后一次下载时间") + private LocalDateTime lastDownloadTime; + + @Schema(description = "文件状态") + @NotNull( + groups = { Insert.class, Update.class }, + message = "文件状态不能为空" + ) + private FileStatusEnum fileStatus; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/enums/FileStatusEnum.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/enums/FileStatusEnum.java new file mode 100644 index 0000000..6eb7fbb --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/enums/FileStatusEnum.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.storage.enums; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文件状态 + * + * @author linck + * @since 2024-04-09 + */ +@Getter +@AllArgsConstructor +public enum FileStatusEnum implements LabelValueInterface { + /** + * 文件上传默认为临时文件,临时文件在到达设置的时间期限后,将会被删除 + */ + TEMP("临时文件", 0), + /** + * 被其他服务确认引用,会转换为正式文件 + */ + CONFIRMED("正式文件", 1); + + private final String label; + + private final Integer value; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/mapperstruct/StorageFileConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/mapperstruct/StorageFileConvert.java new file mode 100644 index 0000000..9935522 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/storage/mapperstruct/StorageFileConvert.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.storage.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 通用文件 MapStruct Convert + * + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface StorageFileConvert { + StorageFileConvert INSTANCE = Mappers.getMapper(StorageFileConvert.class); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDict.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDict.java new file mode 100644 index 0000000..2db3da0 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDict.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.common.enums.YesOrNoEnum; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.OnlyField; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-04-01 + */ +@Data +@Schema(description = "字典") +@EqualsAndHashCode(callSuper = false) +public class SysDict extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键ID", maxLength = 20) + @Length( + groups = { Update.class }, + max = 20, + message = "主键ID长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键ID不能为空") + private String dictId; + + @OnlyField + @Schema(description = "字典名称", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "字典名称长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "字典名称不能为空" + ) + private String dictName; + + @OnlyField + @Schema(description = "字典标识", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "字典标识长度不能大于100" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "字典标识不能为空" + ) + private String dictTag; + + @Schema(description = "描述", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "描述长度不能大于200" + ) + private String remark; + + @Schema(description = "是否系统内置") + private YesOrNoEnum isSystem; + + @Schema(description = "字典项值是否唯一") + private YesOrNoEnum valueIsOnly; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDictItem.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDictItem.java new file mode 100644 index 0000000..01d3fc3 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysDictItem.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.common.enums.YesOrNoEnum; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-04-01 + */ +@Data +@Schema(description = "字典项") +@EqualsAndHashCode(callSuper = false) +public class SysDictItem extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键ID", maxLength = 20) + @Length( + groups = { Update.class }, + max = 20, + message = "主键ID长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键ID不能为空") + private String dictItemId; + + @Schema(description = "字典类别id", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "字典类别id长度不能大于20" + ) + @NotBlank(groups = { Insert.class }, message = "字典类别id不能为空") + private String dictId; + + @Schema(description = "字典项标签", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "字典项标签长度不能大于100" + ) + @NotBlank(groups = { Insert.class }, message = "字典项标签不能为空") + private String label; + + @Schema(description = "字典项值", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "字典项值长度不能大于100" + ) + @NotBlank(groups = { Insert.class }, message = "字典项值不能为空") + private String value; + + @Schema(description = "描述", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "描述长度不能大于200" + ) + private String remark; + + @Schema(description = "是否系统内置") + private YesOrNoEnum isSystem; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysOrg.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysOrg.java new file mode 100644 index 0000000..cad1d9b --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysOrg.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.OnlyField; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-04-09 + */ +@Data +@Schema(description = "机构") +@EqualsAndHashCode(callSuper = false) +public class SysOrg extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "机构ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "机构ID长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "机构ID不能为空") + private String orgId; + + @Schema(description = "机构名称", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "机构名称长度不能大于100" + ) + @NotBlank(groups = { Insert.class }, message = "机构名称不能为空") + private String orgName; + + @OnlyField + @Schema(description = "机构编码", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "机构编码长度不能大于100" + ) + @NotBlank(groups = { Insert.class }, message = "机构编码不能为空") + private String orgCode; + + @Schema(description = "机构类型") + @NotNull( + groups = { Insert.class, Update.class }, + message = "机构类型不能为空" + ) + private Integer orgType; + + @Schema(description = "机构级别") + @NotNull( + groups = { Insert.class, Update.class }, + message = "机构级别不能为空" + ) + private Integer orgLevel; + + @Schema(description = "父机构ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "父机构ID长度不能大于20" + ) + private String parentOrgId; + + @Schema(description = "机构ID路径", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "机构ID路径长度不能大于200" + ) + private String orgIdPath; + + @Schema(description = "排序") + @TableField(value = "\"order\"") + @NotNull(groups = { Update.class }, message = "排序不能为空") private Integer order; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPermission.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPermission.java new file mode 100644 index 0000000..165f2c3 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPermission.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.common.enums.StatusEnum; +import com.zerolinck.passiflora.model.system.enums.PermissionTypeEnum; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.OnlyField; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-05-06 + */ +@Data +@Schema(description = "菜单") +@EqualsAndHashCode(callSuper = false) +public class SysPermission extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "主键长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键不能为空") + private String permissionId; + + @OnlyField + @Schema(description = "标题", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "标题长度不能大于50" + ) + @NotBlank(groups = { Insert.class, Update.class }, message = "标题不能为空") + private String permissionTitle; + + @Schema(description = "名称", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "名称长度不能大于50" + ) + @NotBlank(groups = { Insert.class, Update.class }, message = "名称不能为空") + private String permissionName; + + @Schema(description = "图标", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "图标长度不能大于50" + ) + private String permissionIcon; + + @Schema(description = "上级ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "上级ID长度不能大于20" + ) + private String permissionParentId; + + @Schema(description = "ID路径", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "ID路径长度不能大于200" + ) + private String permissionIdPath; + + @Schema(description = "排序") + @TableField(value = "\"order\"") + @NotNull(groups = { Update.class }, message = "排序不能为空") private Integer order; + + @Schema(description = "备注", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "备注长度不能大于200" + ) + private String remark; + + @Schema(description = "级别") + private Integer permissionLevel; + + @Schema(description = "状态") + @NotNull(groups = { Update.class }, message = "状态不能为空") private StatusEnum permissionStatus; + + @Schema(description = "类型") + @NotNull(groups = { Insert.class, Update.class }, message = "类型不能为空") private PermissionTypeEnum permissionType; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPosition.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPosition.java new file mode 100644 index 0000000..4e3cb47 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPosition.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.common.enums.StatusEnum; +import com.zerolinck.passiflora.model.system.enums.PositionDataScopeTypeEnum; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-05-14 + */ +@Data +@Schema(description = "职位") +@EqualsAndHashCode(callSuper = false) +public class SysPosition extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "职位ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "职位ID长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "职位ID不能为空") + private String positionId; + + @Schema(description = "职位名称", maxLength = 100) + @Length( + groups = { Insert.class, Update.class }, + max = 100, + message = "职位名称长度不能大于100" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "职位名称不能为空" + ) + private String positionName; + + @Schema(description = "职位级别") + private Integer positionLevel; + + @Schema(description = "父职位ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "父职位ID长度不能大于20" + ) + private String parentPositionId; + + @Schema(description = "数据范围类型") + @NotNull( + groups = { Insert.class, Update.class }, + message = "数据范围类型不能为空" + ) + private PositionDataScopeTypeEnum dataScopeType; + + @Schema(description = "职位ID路径", maxLength = 200) + private String positionIdPath; + + @Schema(description = "职位状态") + @NotNull( + groups = { Insert.class, Update.class }, + message = "职位状态不能为空" + ) + private StatusEnum positionStatus; + + @Schema(description = "排序") + @TableField(value = "\"order\"") + @NotNull(groups = { Update.class }, message = "排序不能为空") private Integer order; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionDataScope.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionDataScope.java new file mode 100644 index 0000000..bb512d4 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionDataScope.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-05-14 + */ +@Data +@Schema(description = "职位数据权限") +@EqualsAndHashCode(callSuper = false) +public class SysPositionDataScope extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "主键长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键不能为空") + private String scopeId; + + @Schema(description = "职位ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "职位ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "职位ID不能为空" + ) + private String positionId; + + @Schema(description = "机构ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "机构ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "机构ID不能为空" + ) + private String orgId; + + @Schema(description = "数据范围") + @NotNull( + groups = { Insert.class, Update.class }, + message = "数据范围不能为空" + ) + private Integer dataScope; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionPermission.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionPermission.java new file mode 100644 index 0000000..8624621 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysPositionPermission.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-05-06 + */ +@Data +@Schema(description = "系统职位菜单绑定") +@EqualsAndHashCode(callSuper = false) +public class SysPositionPermission extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "主键长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键不能为空") + private String bindId; + + @Schema(description = "职位ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "职位ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "职位ID不能为空" + ) + private String positionId; + + @Schema(description = "菜单ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "菜单ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "菜单ID不能为空" + ) + private String permissionId; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUser.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUser.java new file mode 100644 index 0000000..ffbe4d4 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUser.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.system.valid.Login; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.OnlyField; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import java.time.LocalDate; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-03-19 + */ +@Data +@Schema(description = "用户") +@EqualsAndHashCode(callSuper = false) +public class SysUser extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键", maxLength = 20) + @Length(groups = { Update.class }, max = 20, message = "主键长度不能大于20") @NotBlank(groups = { Update.class }, message = "主键不能为空") + private String userId; + + @OnlyField + @Schema(description = "用户名", maxLength = 50) + @Length( + groups = { Insert.class }, + max = 50, + message = "用户名长度不能大于50" + ) + @NotBlank(groups = { Insert.class }, message = "用户名不能为空") + private String userName; + + @OnlyField + @Schema(description = "真实姓名", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "用户名长度不能大于50" + ) + @NotBlank(groups = { Insert.class }, message = "用户名不能为空") + private String realName; + + /** 取值参见字典:gender */ + @Schema(description = "性别") + private Integer gender; + + @OnlyField + @Schema(description = "身份证号", maxLength = 50) + @Pattern( + groups = { Insert.class, Update.class }, + regexp = "^\\d{17}[\\dxX]$", + message = "身份证号格式错误" + ) + private String idCardNo; + + @Schema(description = "出生日期") + private LocalDate dateOfBirth; + + @OnlyField + @Schema(description = "手机号", maxLength = 30) + @Length( + groups = { Insert.class, Update.class }, + max = 30, + message = "手机号长度不能大于30" + ) + private String phoneNum; + + @Schema(description = "电子邮箱", maxLength = 50) + @Length( + groups = { Insert.class, Update.class }, + max = 50, + message = "电子邮箱长度不能大于50" + ) + private String email; + + @Schema(description = "备注", maxLength = 200) + @Length( + groups = { Insert.class, Update.class }, + max = 200, + message = "备注长度不能大于200" + ) + private String remark; + + @Schema(description = "用户密码", maxLength = 50) + @Length( + groups = { Login.class }, + max = 50, + message = "用户密码长度不能大于50" + ) + @NotBlank(groups = { Login.class }, message = "用户密码不能为空") + private String userPassword; + + @Schema(description = "密码盐", maxLength = 50) + private String salt; + + @Schema(description = "用户头像", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "用户头像长度不能大于20" + ) + private String avatarFile; + + @Schema(description = "所属机构", maxLength = 20) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "所属机构不能为空" + ) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "用户头像长度不能大于20" + ) + private String orgId; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUserPosition.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUserPosition.java new file mode 100644 index 0000000..2f7e6b8 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/entity/SysUserPosition.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.zerolinck.passiflora.model.common.BaseEntity; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * @author linck + * @since 2024-05-14 + */ +@Data +@Schema(description = "用户职位绑定") +@EqualsAndHashCode(callSuper = false) +public class SysUserPosition extends BaseEntity { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "主键长度不能大于20" + ) + @NotBlank(groups = { Update.class }, message = "主键不能为空") + private String bindId; + + @Schema(description = "用户ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "用户ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "用户ID不能为空" + ) + private String userId; + + @Schema(description = "职位ID", maxLength = 20) + @Length( + groups = { Insert.class, Update.class }, + max = 20, + message = "职位ID长度不能大于20" + ) + @NotBlank( + groups = { Insert.class, Update.class }, + message = "职位ID不能为空" + ) + private String positionId; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PermissionTypeEnum.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PermissionTypeEnum.java new file mode 100644 index 0000000..7fe87be --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PermissionTypeEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.enums; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author linck + * @since 2024-05-08 + */ +@Getter +@AllArgsConstructor +public enum PermissionTypeEnum implements LabelValueInterface { + MENU_SET("目录", 0), + MENU("菜单", 1), + PERMISSION("权限", 2); + + private final String label; + + private final Integer value; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PositionDataScopeTypeEnum.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PositionDataScopeTypeEnum.java new file mode 100644 index 0000000..68fb855 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/enums/PositionDataScopeTypeEnum.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.enums; + +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author linck + * @since 2024-05-29 + */ +@Getter +@AllArgsConstructor +public enum PositionDataScopeTypeEnum implements LabelValueInterface { + USER_ORGANIZATION("用户所属机构", 0), + USER_ORGANIZATION_AND_SUBORDINATES("用户所属机构及其下级机构", 1), + SPECIFIED_ORGANIZATION("指定机构", 2), + SPECIFIED_ORGANIZATION_AND_SUBORDINATES("指定机构及其下级机构", 3), + ALL("全部", 4); + + private final String label; + + private final Integer value; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictConvert.java new file mode 100644 index 0000000..710348d --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictConvert.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +public interface SysDictConvert { + SysDictConvert INSTANCE = Mappers.getMapper(SysDictConvert.class); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictItemConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictItemConvert.java new file mode 100644 index 0000000..02c916b --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysDictItemConvert.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +public interface SysDictItemConvert { + SysDictItemConvert INSTANCE = Mappers.getMapper(SysDictItemConvert.class); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysOrgConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysOrgConvert.java new file mode 100644 index 0000000..027f671 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysOrgConvert.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import com.zerolinck.passiflora.model.system.vo.SysOrgVo; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysOrgConvert { + SysOrgConvert INSTANCE = Mappers.getMapper(SysOrgConvert.class); + + @Mapping( + target = "children", + expression = "java(com.zerolinck.passiflora.model.util.ListUtil.emptyList())" + ) + SysOrgVo entity2Vo(SysOrg sysOrg); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPermissionConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPermissionConvert.java new file mode 100644 index 0000000..3326f15 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPermissionConvert.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import com.zerolinck.passiflora.model.system.vo.SysPermissionVo; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysPermissionConvert { + SysPermissionConvert INSTANCE = Mappers.getMapper( + SysPermissionConvert.class + ); + + @Mapping( + target = "children", + expression = "java(com.zerolinck.passiflora.model.util.ListUtil.emptyList())" + ) + @Mapping(target = "name", source = "permissionName") + @Mapping(target = "meta.title", source = "permissionTitle") + @Mapping(target = "meta.icon", source = "permissionIcon") + @Mapping(target = "meta.order", source = "order") + @Mapping(target = "meta.permissionType", source = "permissionType") + SysPermissionVo entity2vo(SysPermission menu); + + @Mapping( + target = "children", + expression = "java(com.zerolinck.passiflora.model.util.ListUtil.emptyList())" + ) + SysPermissionTableVo entity2tableVo(SysPermission menu); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionConvert.java new file mode 100644 index 0000000..3f0f405 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionConvert.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysPositionConvert { + SysPositionConvert INSTANCE = Mappers.getMapper(SysPositionConvert.class); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionDataScopeConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionDataScopeConvert.java new file mode 100644 index 0000000..12cf33e --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionDataScopeConvert.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysPositionDataScopeConvert { + SysPositionDataScopeConvert INSTANCE = Mappers.getMapper( + SysPositionDataScopeConvert.class + ); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionPermissionConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionPermissionConvert.java new file mode 100644 index 0000000..b646c6e --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysPositionPermissionConvert.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +public interface SysPositionPermissionConvert { + SysPositionPermissionConvert INSTANCE = Mappers.getMapper( + SysPositionPermissionConvert.class + ); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserConvert.java new file mode 100644 index 0000000..fd338ed --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserConvert.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import com.zerolinck.passiflora.model.system.entity.SysUser; +import com.zerolinck.passiflora.model.system.vo.SysUserInfo; +import com.zerolinck.passiflora.model.system.vo.SysUserVo; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysUserConvert { + SysUserConvert INSTANCE = Mappers.getMapper(SysUserConvert.class); + + @Mapping(target = "orgName", ignore = true) + SysUserVo entity2vo(SysUser sysUser); + + @Mapping( + target = "permission", + expression = "java(com.zerolinck.passiflora.model.util.ListUtil.emptyList())" + ) + @Mapping( + target = "menu", + expression = "java(com.zerolinck.passiflora.model.util.ListUtil.emptyList())" + ) + @Mapping(target = "salt", ignore = true) + @Mapping(target = "userPassword", ignore = true) + SysUserInfo entity2info(SysUser sysUser); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserPositionConvert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserPositionConvert.java new file mode 100644 index 0000000..8f32e15 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/mapperstruct/SysUserPositionConvert.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.mapperstruct; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author linck + * @since 2024-03-26 + */ +@Mapper +@SuppressWarnings("all") +public interface SysUserPositionConvert { + SysUserPositionConvert INSTANCE = Mappers.getMapper( + SysUserPositionConvert.class + ); +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/valid/Login.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/valid/Login.java new file mode 100644 index 0000000..0c97dd1 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/valid/Login.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.valid; + +/** + * @author linck + * @since 2024-02-07 + */ +public interface Login {} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysOrgVo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysOrgVo.java new file mode 100644 index 0000000..ea1239e --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysOrgVo.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author linck + * @since 2024-04-09 + */ +@Data +@Schema(description = "机构") +@EqualsAndHashCode(callSuper = true) +public class SysOrgVo extends SysOrg { + + List children; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionTableVo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionTableVo.java new file mode 100644 index 0000000..5bd7c0c --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionTableVo.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author linck + * @since 2024-05-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysPermissionTableVo extends SysPermission { + + private List children; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionVo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionVo.java new file mode 100644 index 0000000..7997438 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPermissionVo.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.enums.PermissionTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Data; + +/** + * @author linck + * @since 2024-05-06 + */ +@Data +public class SysPermissionVo { + + private String permissionId; + + @Schema(description = "名称", maxLength = 50) + private String name; + + private String permissionParentId; + + private MenuMeta meta; + + private List children; + + @Data + public static class MenuMeta { + + private String title; + + private String icon; + + private String order; + + @Schema(description = "类型") + private PermissionTypeEnum permissionType; + } +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPositionVo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPositionVo.java new file mode 100644 index 0000000..dd3cb80 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysPositionVo.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author linck + * @since 2024-05-28 + */ +@Data +@Schema(description = "职位") +@EqualsAndHashCode(callSuper = true) +public class SysPositionVo extends SysPosition { + + private List children; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserInfo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserInfo.java new file mode 100644 index 0000000..3554425 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.entity.SysUser; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author linck + * @since 2024-03-19 + */ +@Data +@Schema(description = "用户") +@EqualsAndHashCode(callSuper = false) +public class SysUserInfo extends SysUser { + + private List permission; + + private List menu; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserVo.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserVo.java new file mode 100644 index 0000000..073ca98 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/system/vo/SysUserVo.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.system.vo; + +import com.zerolinck.passiflora.model.system.entity.SysUser; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author linck + * @since 2024-04-29 + */ +@Data +@Schema(description = "用户") +@EqualsAndHashCode(callSuper = true) +public class SysUserVo extends SysUser { + + @Schema(description = "机构名称") + private String orgName; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/util/ListUtil.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/util/ListUtil.java new file mode 100644 index 0000000..e3bcf1a --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/util/ListUtil.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author linck + * @since 2024-05-01 + */ +public class ListUtil { + + @SuppressWarnings("rawtypes") + public static List emptyList() { + return new ArrayList<>(); + } +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Insert.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Insert.java new file mode 100644 index 0000000..7262e4f --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Insert.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.valid; + +/** + * @author linck + * @since 2024-02-06 + */ +public interface Insert {} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/OnlyField.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/OnlyField.java new file mode 100644 index 0000000..197da4c --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/OnlyField.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.valid; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 唯一值检测注解,显示名称,根据 swagger 注解来,如果没有 swagger 注解,则显示字段英文名称 + * + *

只支持单一字段校验,不支持联合字段唯一校验 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface OnlyField { + /** 自定义错误返回信息 */ + String message() default ""; +} diff --git a/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Update.java b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Update.java new file mode 100644 index 0000000..46a3c26 --- /dev/null +++ b/passiflora-server/modules/passiflora-model/src/main/java/com/zerolinck/passiflora/model/valid/Update.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.model.valid; + +/** + * @author linck + * @since 2024-02-06 + */ +public interface Update {} diff --git a/passiflora-server/passiflora-gateway-app/build.gradle b/passiflora-server/passiflora-gateway-app/build.gradle new file mode 100644 index 0000000..550ac72 --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/build.gradle @@ -0,0 +1,56 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + id 'java' + id 'org.springframework.boot' version '3.3.0' +} + +configurations { + all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' +} + +ext { + // 默认是开发环境;切换生产环境使用 gradle -Denv=prod + environment = System.getProperty("env", "dev") + print('使用 ' + environment + ' 环境编译') + configFile = file('../config.groovy') + configMap = new ConfigSlurper(environment).parse(configFile.toURI().toURL()).toProperties() + configMap['projectVersion'] = project.version + configMap['env'] = environment + configMap['isProd'] = (environment == 'prod') ? 'true' : 'false' + configMap['isDev'] = (environment == 'dev') ? 'true' : 'false' +} + +dependencies { + implementation project(':modules:passiflora-feign') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + implementation 'org.springframework.cloud:spring-cloud-starter-gateway' + implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery' + implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer' + + // 非开发环境,排除 Swagger UI + if (configMap['isDev'] == 'true') { + implementation 'com.github.xiaoymin:knife4j-gateway-spring-boot-starter:4.4.0' + } +} + +jar { + enabled = false +} + +processResources { + // src/main/resources 下的文件中 @key@ 的内容使用 config.groovy 里对应的进行替换 + from('src/main/resources') { + filter(ReplaceTokens, tokens: configMap) + duplicatesStrategy(DuplicatesStrategy.INCLUDE) + } +} \ No newline at end of file diff --git a/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/PassifloraGatewayApplication.java b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/PassifloraGatewayApplication.java new file mode 100644 index 0000000..50c8d52 --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/PassifloraGatewayApplication.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.gateway; + +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.util.NetUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +@Slf4j +@SpringBootApplication +@EnableConfigurationProperties(PassifloraProperties.class) +public class PassifloraGatewayApplication { + + @SneakyThrows + public static void main(String[] args) { + ConfigurableApplicationContext application = SpringApplication.run( + PassifloraGatewayApplication.class, + args + ); + ConfigurableEnvironment environment = application.getEnvironment(); + String projectVersion = environment.getProperty( + "passiflora.project-version" + ); + String env = environment.getProperty("passiflora.env"); + String port = environment.getProperty("server.port"); + String outIp = NetUtil.findOutIp(); + log.info( + """ + \n项目启动成功: {} 环境 ◉‿◉ + 本地聚合 Swagger: \t\thttp://localhost:{}/doc.html + 外部地址聚合 Swagger: \thttp://{}:{}/doc.html + Passiflora (C) 2024 version: {}""", + env, + port, + outIp, + port, + projectVersion + ); + } +} diff --git a/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GatewayConfiguration.java b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GatewayConfiguration.java new file mode 100644 index 0000000..ba92068 --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GatewayConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.gateway.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancedExchangeFilterFunction; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +/** 网关配置 */ +@Configuration(proxyBeanMethods = false) +public class GatewayConfiguration { + + @Bean + public GlobalExceptionHandler globalExceptionHandler( + ObjectMapper objectMapper + ) { + return new GlobalExceptionHandler(objectMapper); + } + + @Bean + public WebClient.Builder webClientBuilder( + LoadBalancedExchangeFilterFunction balancedExchangeFilterFunction + ) { + return WebClient.builder().filter(balancedExchangeFilterFunction); + } +} diff --git a/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GlobalExceptionHandler.java b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..4bd586c --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/config/GlobalExceptionHandler.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.gateway.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.exception.BizException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.reactive.resource.NoResourceFoundException; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.handler.ResponseStatusExceptionHandler; +import reactor.core.publisher.Mono; + +/** 网关异常通用处理器,只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行 */ +@Slf4j +@Order(-1) +@RequiredArgsConstructor +public class GlobalExceptionHandler implements ErrorWebExceptionHandler { + + private final ObjectMapper objectMapper; + + @Override + public Mono handle(ServerWebExchange exchange, Throwable throwable) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + + if (response.isCommitted()) { + return Mono.error(throwable); + } + + // header set + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + return handleException(request, response, throwable) + .flatMap(result -> + response.writeWith( + Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = + response.bufferFactory(); + try { + return bufferFactory.wrap( + objectMapper.writeValueAsBytes(result) + ); + } catch (JsonProcessingException e) { + log.error("Error writing response", e); + return bufferFactory.wrap(new byte[0]); + } + }) + ) + ); + } + + private Mono> handleException( + ServerHttpRequest request, + ServerHttpResponse response, + Throwable throwable + ) { + if (throwable instanceof ResponseStatusException e) { + response.setStatusCode(e.getStatusCode()); + logException(request, e); + if (HttpStatusCode.valueOf(404).equals(e.getStatusCode())) { + return Mono.just(Result.failed(ResultCodeEnum.NOT_FOUND)); + } + return Mono.just(Result.failed(e.getReason())); + } else { + if (throwable instanceof BizException e) { + logException(request, e); + return Mono.just(Result.failed(e.getCode(), e.getMessage())); + } else { + logException(request, throwable); + return Mono.just(Result.failed(ResultCodeEnum.FAILED)); + } + } + } + + private void logException(ServerHttpRequest request, Throwable throwable) { + if (throwable instanceof BizException) { + log.warn( + "Business exception: path={}, exception={}", + request.getPath(), + throwable.getMessage() + ); + } else if (throwable instanceof NoResourceFoundException) { + log.warn( + "NoResourceFoundException: path={}, exception={}", + request.getPath(), + throwable.getMessage() + ); + }else { + log.error( + "Unexpected exception: path={}", + request.getPath(), + throwable + ); + } + } +} diff --git a/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/ApiLoggingFilter.java b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/ApiLoggingFilter.java new file mode 100644 index 0000000..2180f7d --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/ApiLoggingFilter.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.gateway.filter; + +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 全局拦截器,作用所有的微服务 + * + *

1. 对请求的API调用过滤,记录接口的请求时间,方便日志审计、告警、分析等运维操作 + * + *

+ */ +@Slf4j +@Component +public class ApiLoggingFilter implements GlobalFilter, Ordered { + + private static final String START_TIME = "startTime"; + + // nginx 需要配置 + private static final String X_FORWARDED_FOR = "X-Forwarded-For"; + + @Override + public Mono filter( + ServerWebExchange exchange, + GatewayFilterChain chain + ) { + exchange.getAttributes().put(START_TIME, System.currentTimeMillis()); + return chain.filter(exchange).then(logRequest(exchange)); + } + + private Mono logRequest(ServerWebExchange exchange) { + return logRequestSummary(exchange) + .flatMap(clientIpAddress -> + logRequestDetails(exchange, clientIpAddress) + ); + } + + private Mono logRequestSummary(ServerWebExchange exchange) { + return Mono.fromSupplier(() -> { + ServerHttpRequest request = exchange.getRequest(); + if (log.isDebugEnabled()) { + log.debug( + "Received request: {}:{} {}", + request.getMethod(), + request.getURI(), + request.getHeaders() + ); + } + return resolveClientIpAddress(request); + }); + } + + private String resolveClientIpAddress(ServerHttpRequest request) { + String clientIpAddress = request.getHeaders().getFirst(X_FORWARDED_FOR); + if (clientIpAddress == null) { + if (log.isDebugEnabled()) { + log.debug("X-Forwarded-For 未配置"); + } + clientIpAddress = + Objects.requireNonNull(request.getRemoteAddress()).getHostString(); + } else { + clientIpAddress = clientIpAddress.split(",")[0]; + } + return clientIpAddress; + } + + private Mono logRequestDetails( + ServerWebExchange exchange, + String clientIpAddress + ) { + return Mono.fromRunnable(() -> { + Long startTime = exchange.getAttribute(START_TIME); + if (startTime != null) { + Long executionTime = System.currentTimeMillis() - startTime; + HttpStatusCode statusCode = exchange + .getResponse() + .getStatusCode(); + log.info( + "Request from {} to {} returned with status {} in {} ms", + clientIpAddress, + exchange.getRequest().getURI(), + statusCode, + executionTime + ); + } + }); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } +} diff --git a/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/TokenCheckGatewayFilterFactory.java b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/TokenCheckGatewayFilterFactory.java new file mode 100644 index 0000000..65d855a --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/java/com/zerolinck/passiflora/gateway/filter/TokenCheckGatewayFilterFactory.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.gateway.filter; + +import cn.hutool.core.collection.CollectionUtil; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.model.common.constant.Constants; +import jakarta.annotation.Resource; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.RequestPath; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +/** + * @author linck + * @since 2024-04-30 + */ +@Slf4j +@Component +public class TokenCheckGatewayFilterFactory + extends AbstractGatewayFilterFactory< + TokenCheckGatewayFilterFactory.Config + > { + + private static final Set UN_CHECK_PATH = new HashSet<>(); + + static { + UN_CHECK_PATH.add( + Pattern.compile("/passiflora/system-api/sysUser/login") + ); + UN_CHECK_PATH.add( + Pattern.compile("/passiflora/system-api/sysUser/logout") + ); + UN_CHECK_PATH.add(Pattern.compile("/doc\\.html")); + UN_CHECK_PATH.add(Pattern.compile(".*/v3/api-docs")); + } + + public TokenCheckGatewayFilterFactory() { + super(TokenCheckGatewayFilterFactory.Config.class); + } + + @Resource + private WebClient.Builder webBuilder; + + @Override + public GatewayFilter apply(Config config) { + return ( + (exchange, chain) -> { + RequestPath path = exchange.getRequest().getPath(); + for (Pattern pattern : UN_CHECK_PATH) { + if (pattern.matcher(path.value()).matches()) { + return chain.filter(exchange); + } + } + + HttpHeaders headers = exchange.getRequest().getHeaders(); + List tokens = headers.get(Constants.Authorization); + String token = ""; + if (CollectionUtil.isNotEmpty(tokens)) { + token = tokens.getFirst(); + } + return webBuilder + .baseUrl("http://passiflora-system-app") + .build() + .get() + .uri("/passiflora/system-api/sysUser/checkToken") + .header(Constants.Authorization, token) + .retrieve() + .bodyToMono( + new ParameterizedTypeReference>() {} + ) + .flatMap(result -> { + if ( + ResultCodeEnum.SUCCESS.getCode() != + result.getCode() || + !result.getData() + ) { + return Mono.error( + new BizException(ResultCodeEnum.UNAUTHORIZED) + ); + } else { + return chain.filter(exchange); + } + }) + .onErrorResume(ex -> { + if (ex instanceof BizException) { + return Mono.error(ex); + } + return Mono.error(new BizException(ex)); + }); + } + ); + } + + /** 配置参数类 */ + @Getter + @Slf4j + public static class Config {} +} diff --git a/passiflora-server/passiflora-gateway-app/src/main/resources/application.yml b/passiflora-server/passiflora-gateway-app/src/main/resources/application.yml new file mode 100644 index 0000000..f1e7866 --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/resources/application.yml @@ -0,0 +1,48 @@ +server: + port: 51000 + +spring: + main: + web-application-type: reactive + application: + name: passiflora-gateway-app + profiles: + active: @env@ + cloud: + nacos: + discovery: + server-addr: @nacos.serverAddr@ + gateway: + discovery: + locator: + enabled: true + routes: + - id: passiflora-system-app + uri: lb://passiflora-system-app + predicates: + - Path=/passiflora/system-api/** + - id: passiflora-storage-app + uri: lb://passiflora-storage-app + predicates: + - Path=/passiflora/storage-api/** + + default-filters: + - TokenCheck #添加默认 Filter TokenCheckGatewayFilterFactory + +knife4j: + gateway: + enabled: true + strategy: manual + routes: + - name: 系统服务 + service-name: passiflora-system-app + url: /passiflora/system-api/v3/api-docs?group=default + context-path: /passiflora/system-api + - name: 文件服务 + service-name: passiflora-storage-app + url: /passiflora/storage-api/v3/api-docs?group=default + context-path: /passiflora/storage-api + +passiflora: + env: @env@ + project-version: @projectVersion@ \ No newline at end of file diff --git a/passiflora-server/passiflora-gateway-app/src/main/resources/log4j2-spring.xml b/passiflora-server/passiflora-gateway-app/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..bfcceda --- /dev/null +++ b/passiflora-server/passiflora-gateway-app/src/main/resources/log4j2-spring.xml @@ -0,0 +1,65 @@ + + + + + + + + + + ${spring:spring.application.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/passiflora-server/passiflora-storage-app/build.gradle b/passiflora-server/passiflora-storage-app/build.gradle new file mode 100644 index 0000000..57e8723 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/build.gradle @@ -0,0 +1,113 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + id 'java' + id 'jacoco' + id 'org.liquibase.gradle' version '2.2.1' + id 'org.springframework.boot' version '3.3.0' +} + +configurations { + all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' +} + +ext { + // 默认是开发环境;切换生产环境使用 gradle -Denv=prod + environment = System.getProperty("env", "dev") + print('使用 ' + environment + ' 环境编译') + configFile = file('../config.groovy') + configMap = new ConfigSlurper(environment).parse(configFile.toURI().toURL()).toProperties() + configMap['projectVersion'] = project.version + configMap['env'] = environment + configMap['isProd'] = (environment == 'prod') ? 'true' : 'false' + configMap['isDev'] = (environment == 'dev') ? 'true' : 'false' +} + +dependencies { + implementation project(':modules:passiflora-feign') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + liquibaseRuntime platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // 非开发环境,排除 Swagger UI + if (configMap['isDev'] == 'true') { + implementation 'com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter' + } else { + implementation('com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter') { + exclude group: 'com.github.xiaoymin', module: 'knife4j-openapi3-ui' + exclude group: 'org.webjars', module: 'swagger-ui' + } + } + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer' + implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery' + implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer' + + implementation 'org.redisson:redisson-spring-boot-starter' + implementation 'com.baomidou:mybatis-plus-boot-starter' + implementation 'cn.hutool:hutool-crypto' + implementation 'cn.hutool:hutool-http' + implementation 'org.postgresql:postgresql' + + // liquibase + liquibaseRuntime 'org.liquibase:liquibase-core' + liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl' + liquibaseRuntime 'info.picocli:picocli' + liquibaseRuntime 'org.postgresql:postgresql' + + implementation 'io.minio:minio' +} + +test { + useJUnitPlatform() +} + +compileJava { + doLast { + copy { + from("src/main/java") { + include("**/*Mapper.xml") + } + into("build/classes/java/main") + } + } +} + +jar { + enabled = false +} + +processResources { + // src/main/resources 下的文件中 @key@ 的内容使用 config.groovy 里对应的进行替换 + from('src/main/resources') { + filter(ReplaceTokens, tokens: configMap) + duplicatesStrategy(DuplicatesStrategy.INCLUDE) + } + exclude 'db/**' +} + +liquibase { + activities { + main { + changelogFile 'src/main/resources/db/main.yml' + url configMap.'storage_app.database.url' + username configMap.'storage_app.database.username' + password configMap.'storage_app.database.username' + logLevel "info" + } + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/PassifloraStorageApplication.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/PassifloraStorageApplication.java new file mode 100644 index 0000000..ee276da --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/PassifloraStorageApplication.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage; + +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.util.NetUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +@Slf4j +@SpringBootApplication +@MapperScan("com.zerolinck.**.mapper") +@EnableConfigurationProperties(PassifloraProperties.class) +@EnableFeignClients(basePackages = { "com.zerolinck.passiflora.feign.**" }) +public class PassifloraStorageApplication { + + @SneakyThrows + public static void main(String[] args) { + ConfigurableApplicationContext application = SpringApplication.run( + PassifloraStorageApplication.class, + args + ); + ConfigurableEnvironment environment = application.getEnvironment(); + String projectVersion = environment.getProperty( + "passiflora.project-version" + ); + String env = environment.getProperty("passiflora.env"); + String port = environment.getProperty("server.port"); + String path = environment.getProperty("server.servlet.context-path"); + String outIp = NetUtil.findOutIp(); + log.info( + """ + \n项目启动成功: {} 环境 ◉‿◉ + 本地 Swagger: \t\thttp://localhost:{}{}/doc.html + 外部地址 Swagger: \thttp://{}:{}{}/doc.html + Passiflora (C) 2024 version: {}""", + env, + port, + path, + outIp, + port, + path, + projectVersion + ); + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/config/MinioConfig.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/config/MinioConfig.java new file mode 100644 index 0000000..3541cc6 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/config/MinioConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage.config; + +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import io.minio.MinioClient; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author linck + * @since 2023-12-15 + */ +@Configuration +public class MinioConfig { + + @Resource + private PassifloraProperties passifloraProperties; + + /** + * 注入minio 客户端 + */ + @Bean + public MinioClient minioClient() { + String endpoint = passifloraProperties + .getStorage() + .getMinio() + .getEndpoint(); + String accessKey = passifloraProperties + .getStorage() + .getMinio() + .getAccessKey(); + String secretKey = passifloraProperties + .getStorage() + .getMinio() + .getSecretKey(); + return MinioClient + .builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/controller/StorageFileController.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/controller/StorageFileController.java new file mode 100644 index 0000000..1ed8267 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/controller/StorageFileController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage.controller; + +import cn.hutool.core.util.StrUtil; +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.storage.StorageFileApi; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import com.zerolinck.passiflora.storage.service.StorageFileService; +import jakarta.annotation.Resource; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 通用文件 Controller + * + * @author linck + * @since 2024-05-17 + */ +@Slf4j +@RestController +@RequestMapping("storageFile") +public class StorageFileController implements StorageFileApi { + + @Resource + private StorageFileService storageFileService; + + @Override + public Result> page( + QueryCondition condition + ) { + return Result.page(storageFileService.page(condition)); + } + + @Override + public Result> listByFileIds(List fileIds) { + return Result.ok(storageFileService.listByFileIds(fileIds)); + } + + /** + * 尝试文件秒传 + * + * @param storageFile 需要参数 originalFileName,contentType, fileMd5 + * @return 空字符串表示无法秒传,应再次调用文件上传接口;有值字符串表示上传成功,上传文件ID + */ + @Override + public Result tryQuicklyUpload(StorageFile storageFile) { + if (StrUtil.isBlank(storageFile.getOriginalFileName())) { + throw new BizException("文件名称不能为空,请重新填写"); + } + if (StrUtil.isBlank(storageFile.getContentType())) { + throw new BizException("文件 contentType 不能为空,请重新填写"); + } + if (StrUtil.isBlank(storageFile.getFileMd5())) { + throw new BizException("文件MD5不能为空,请重新填写"); + } + return Result.ok(storageFileService.tryQuicklyUpload(storageFile)); + } + + @Override + public Result upload(MultipartFile file, String fileName) { + return Result.ok(storageFileService.upload(file, fileName)); + } + + @Override + public Result detail(String fileId) { + AssertUtil.notBlank(fileId); + return Result.ok(storageFileService.detail(fileId)); + } + + @Override + public Result delete(List fileIds) { + AssertUtil.notEmpty(fileIds); + storageFileService.deleteByIds(fileIds); + return Result.ok(); + } + + @Override + public void downloadFile(String fileId) { + storageFileService.downloadFile(fileId); + } + + @Override + public void downloadZip(List fileIds) { + storageFileService.downloadZip(fileIds); + } + + @Override + public Result confirmFile(List fileIds) { + storageFileService.confirmFile(fileIds); + return Result.ok(); + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/init/CreateBucketRunner.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/init/CreateBucketRunner.java new file mode 100644 index 0000000..7d6e9d7 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/init/CreateBucketRunner.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage.init; + +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import io.minio.BucketExistsArgs; +import io.minio.MakeBucketArgs; +import io.minio.MinioClient; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +/** + * 检查 bucket 是否存在,不存在,则创建 + * + * @author linck + * @since 2024-05-27 + */ +@Slf4j +@Component +public class CreateBucketRunner implements ApplicationRunner { + + @Resource + private MinioClient minioClient; + + @Resource + private PassifloraProperties passifloraProperties; + + @Override + public void run(ApplicationArguments args) { + String bucketName = passifloraProperties.getStorage().getBucketName(); + LockUtil.lockAndTransactionalLogic( + "passiflora:storage:bucket:", + new LockWrapper(), + () -> { + try { + boolean bucketExists = minioClient.bucketExists( + BucketExistsArgs.builder().bucket(bucketName).build() + ); + if (log.isDebugEnabled()) { + log.debug( + "bucket: {}, 是否存在: {}", + bucketName, + bucketExists + ); + } + if (!bucketExists) { + minioClient.makeBucket( + MakeBucketArgs.builder().bucket(bucketName).build() + ); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + ); + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/StorageFileMapper.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/StorageFileMapper.java new file mode 100644 index 0000000..76fd4ed --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/StorageFileMapper.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * 通用文件 Mybatis Mapper + * + * @author linck + * @since 2024-05-17 + */ +public interface StorageFileMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** + * 使用更新删除,保证 update_by 和 update_time 正确 + */ + int deleteByIds( + @Param("fileIds") Collection fileIds, + @Param("updateBy") String updateBy + ); + + @Select( + "SELECT * FROM storage_file WHERE file_md5 = #{fileMd5} AND del_flag = 0" + ) + List listByFileMd5(@Param("fileMd5") String fileMd5); + + @Select( + "SELECT count(*) FROM storage_file WHERE file_md5 = #{fileMd5} AND del_flag = 0" + ) + Integer countByFileMd5(@Param("fileMd5") String fileMd5); + + @Update( + "UPDATE storage_file SET last_download_time = now(), download_count = download_count + 1 WHERE file_id = #{fileId}" + ) + void incrDownCount(@Param("fileId") String fileId); + + int confirmFile( + @Param("fileIds") Collection fileIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/xml/StorageFileMapper.xml b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/xml/StorageFileMapper.xml new file mode 100644 index 0000000..8fc7ea9 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/mapper/xml/StorageFileMapper.xml @@ -0,0 +1,46 @@ + + + + + + + file_id, original_file_name, file_purpose, bucket_name, object_name, file_size, content_type, file_md5, download_count, last_download_time, file_status, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE storage_file SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE file_id IN + + #{id} + + + + + UPDATE storage_file SET update_time = now(), + update_by = #{updateBy}, + file_status = 1 + WHERE file_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/service/StorageFileService.java b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/service/StorageFileService.java new file mode 100644 index 0000000..e191b1d --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/java/com/zerolinck/passiflora/storage/service/StorageFileService.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage.service; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.net.URLEncodeUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.http.Header; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.NetUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import com.zerolinck.passiflora.model.storage.enums.FileStatusEnum; +import com.zerolinck.passiflora.storage.mapper.StorageFileMapper; +import io.minio.*; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.MimeTypeUtils; +import org.springframework.web.multipart.MultipartFile; + +/** + * 通用文件 Service + * + * @author linck + * @since 2024-05-17 + */ +@Slf4j +@Service +public class StorageFileService + extends ServiceImpl { + + @Resource + private MinioClient minioClient; + + @Resource + private PassifloraProperties passifloraProperties; + + private static final String LOCK_KEY = "passiflora:lock:storageFile:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(StorageFile.class), + condition.sortWrapper(StorageFile.class) + ); + } + + public List listByFileIds(List fileIds) { + return baseMapper.selectList( + new LambdaQueryWrapper() + .in(StorageFile::getFileId, fileIds) + ); + } + + /** + * 尝试文件秒传 + * + * @param storageFile 需要参数 originalFileName,contentType, fileMd5 + * @return 空字符串表示无法秒传,应再次调用文件上传接口;有值字符串表示上传成功,上传文件ID + */ + public String tryQuicklyUpload(StorageFile storageFile) { + return (String) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(StorageFile::getFileMd5, storageFile.getFileMd5()), + () -> { + List storageFiles = listByFileMd5( + storageFile.getFileMd5() + ); + if (CollectionUtil.isEmpty(storageFiles)) { + return ""; + } + StorageFile dbFile = storageFiles.getFirst(); + dbFile.setFileId(null); + dbFile.setFilePurpose(null); + dbFile.setDownloadCount(null); + dbFile.setLastDownloadTime(null); + dbFile.setFileStatus(FileStatusEnum.TEMP); + dbFile.setOriginalFileName(storageFile.getOriginalFileName()); + dbFile.setContentType(storageFile.getContentType()); + baseMapper.insert(dbFile); + return dbFile.getFileId(); + } + ); + } + + @SneakyThrows + public String upload(MultipartFile file, String fileName) { + String md5Hex = DigestUtil.md5Hex(file.getBytes()); + return (String) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(StorageFile::getFileMd5, md5Hex), + () -> { + String bucket = passifloraProperties + .getStorage() + .getBucketName(); + StorageFile storageFile = new StorageFile(); + storageFile.setOriginalFileName(file.getOriginalFilename()); + if (StrUtil.isNotBlank(fileName)) { + storageFile.setOriginalFileName(fileName); + } + storageFile.setContentType(file.getContentType()); + storageFile.setFileMd5(md5Hex); + storageFile.setFileSize(file.getSize()); + storageFile.setFileStatus(FileStatusEnum.TEMP); + String extName = FileUtil.extName( + storageFile.getOriginalFileName() + ); + String objectName = md5Hex + "." + extName; + storageFile.setBucketName(bucket); + storageFile.setObjectName(objectName); + String tryQuicklyUploadResult = tryQuicklyUpload(storageFile); + if (StrUtil.isNotBlank(tryQuicklyUploadResult)) { + return tryQuicklyUploadResult; + } + + // 检查文件是否已经上传 + List storageFiles = baseMapper.selectList( + new LambdaQueryWrapper() + .eq(StorageFile::getFileMd5, md5Hex) + ); + StatObjectResponse statObject = null; + if (!storageFiles.isEmpty()) { + StorageFile dbStorageFile = storageFiles.getFirst(); + objectName = dbStorageFile.getObjectName(); + bucket = dbStorageFile.getBucketName(); + // 检查 minio 文件是否存在 + try { + statObject = + minioClient.statObject( + StatObjectArgs + .builder() + .bucket(dbStorageFile.getBucketName()) + .object(dbStorageFile.getObjectName()) + .build() + ); + log.info("文件已存在,秒传,fileMd5: {}", md5Hex); + } catch (Exception e) { + // 可能查询文件不存在,尝试重新覆盖文件 + log.warn( + "minio stat 文件错误 objectName: {}, exception: {}", + dbStorageFile.getObjectName(), + e.getMessage() + ); + } + } + + if (statObject == null) { + try { + minioClient.putObject( + PutObjectArgs + .builder() + .bucket(bucket) + .object(objectName) + .stream( + file.getInputStream(), + file.getInputStream().available(), + -1 + ) + .contentType(file.getContentType()) + .build() + ); + } catch (Exception e) { + throw new BizException(e); + } + } + this.save(storageFile); + return storageFile.getFileId(); + } + ); + } + + public List listByFileMd5(String fileMd5) { + return baseMapper.listByFileMd5(fileMd5); + } + + public void deleteByIds(Collection fileIds) { + for (String fileId : fileIds) { + deleteById(fileId); + } + } + + @SneakyThrows + public void deleteById(String fileId) { + if (StrUtil.isBlank(fileId)) { + return; + } + StorageFile storageFile = baseMapper.selectById(fileId); + if (storageFile == null) { + return; + } + LockUtil.lockAndTransactionalLogic( + LOCK_KEY + "md5:", + new LockWrapper() + .lock(StorageFile::getFileMd5, storageFile.getFileMd5()), + () -> { + baseMapper.deleteByIds( + List.of(fileId), + CurrentUtil.getCurrentUserId() + ); + Integer md5Count = baseMapper.countByFileMd5( + storageFile.getFileMd5() + ); + if (md5Count == 0) { + try { + minioClient.removeObject( + RemoveObjectArgs + .builder() + .bucket(storageFile.getBucketName()) + .object(storageFile.getObjectName()) + .build() + ); + } catch (Exception e) { + throw new BizException(e); + } + } + return null; + } + ); + } + + public StorageFile detail(String fileId) { + StorageFile storageFile = baseMapper.selectById(fileId); + if (storageFile == null) { + throw new BizException("无对应通用文件数据,请刷新后重试"); + } + return storageFile; + } + + @SneakyThrows + public void downloadFile(String fileId) { + StorageFile storageFile = baseMapper.selectById(fileId); + if (storageFile == null) { + throw new BizException("文件消失了"); + } + GetObjectResponse inputStream = minioClient.getObject( + GetObjectArgs + .builder() + .bucket(storageFile.getBucketName()) + .object(storageFile.getObjectName()) + .build() + ); + HttpServletResponse response = NetUtil.getResponse(); + if ( + MimeTypeUtils.APPLICATION_JSON + .toString() + .equals(storageFile.getContentType()) + ) { + response.setContentType(MimeTypeUtils.TEXT_PLAIN.toString()); + } else { + response.setContentType(storageFile.getContentType()); + } + response.setHeader( + Header.CONTENT_DISPOSITION.getValue(), + "attachment; filename=" + + URLEncodeUtil.encode(storageFile.getOriginalFileName()) + ); + IOUtils.copy(inputStream, response.getOutputStream()); + baseMapper.incrDownCount(fileId); + } + + @SneakyThrows + public void downloadZip(List fileIds) { + NetUtil + .getResponse() + .setHeader( + Header.CONTENT_DISPOSITION.getValue(), + "attachment; filename=" + URLEncodeUtil.encode("文件压缩包.zip") + ); + ZipOutputStream zipOut = new ZipOutputStream( + NetUtil.getResponse().getOutputStream() + ); + Set fileNameSet = new HashSet<>(); + for (String fileId : fileIds) { + StorageFile storageFile = baseMapper.selectById(fileId); + + // 出现同名文件时重命名 + String fileName = dealFileName(storageFile, fileNameSet); + fileNameSet.add(fileName); + zipOut.putNextEntry(new ZipEntry(fileName)); + GetObjectResponse inputStream = minioClient.getObject( + GetObjectArgs + .builder() + .bucket(storageFile.getBucketName()) + .object(storageFile.getObjectName()) + .build() + ); + IOUtils.copy(inputStream, zipOut); + inputStream.close(); + zipOut.closeEntry(); + baseMapper.incrDownCount(fileId); + } + zipOut.close(); + } + + public void confirmFile(List fileIds) { + baseMapper.confirmFile(fileIds, CurrentUtil.getCurrentUserId()); + } + + /** + * 处理压缩包文件重名问题 + */ + private static String dealFileName( + StorageFile storageFile, + Set fileNameSet + ) { + String fileName = storageFile.getOriginalFileName(); + while (fileNameSet.contains(fileName)) { + String[] fileNames = fileName.split("\\."); + if (fileNames.length == 1) { + fileName += "-1"; + continue; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fileNames.length - 1; i++) { + sb.append(fileNames[i]); + if (i != fileNames.length - 2) { + sb.append("."); + } + } + sb.append("-1"); + sb.append("."); + sb.append(fileNames[fileNames.length - 1]); + fileName = sb.toString(); + } + return fileName; + } +} diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/application.yml b/passiflora-server/passiflora-storage-app/src/main/resources/application.yml new file mode 100644 index 0000000..fb439b0 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/application.yml @@ -0,0 +1,67 @@ +server: + port: 51002 + servlet: + context-path: /passiflora/storage-api + +spring: + application: + name: passiflora-storage-app + profiles: + active: @env@ + cloud: + nacos: + discovery: + server-addr: @nacos.serverAddr@ + datasource: + username: @storage_app.database.username@ + password: @storage_app.database.password@ + url: @storage_app.database.url@ + driver-class-name: org.postgresql.Driver + data: + redis: + database: @redis.database@ + host: @redis.host@ + port: @redis.port@ + password: @redis.password@ + +feign: + compression: + request: + enabled: true + mime-types: text/html,application/xml,application/json + min-request-size: 2048 + response: + enabled: true + client: + config: + default: + connect-timeout: 5000 + read-timeout: 600000 + +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + mapper-locations: classpath*:com/zerolinck/**/mapper/xml/*Mapper.xml + global-config: + db-config: + logic-delete-value: 1 + logic-not-delete-value: 0 + insert-strategy: not_null + update-strategy: not_null + +passiflora: + env: @env@ + config: + mybatis: true + exception: true + globalFilter: true + lock: true + cache: true + project-version: @projectVersion@ + + storage: + bucket-name: passiflora + minio: + endpoint: @minio.endpoint@ + access-key: @minio.accessKey@ + secret-key: @minio.secretKey@ diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_create_table_file.yml b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_create_table_file.yml new file mode 100644 index 0000000..5b38b94 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_create_table_file.yml @@ -0,0 +1,113 @@ +databaseChangeLog: + - changeSet: + id: 202405172001_create_table_file.yml + author: linck + changes: + - createTable: + tableName: storage_file + remarks: 通用文件 + columns: + - column: + name: file_id + type: varchar(20) + constraints: + primaryKey: true + nullable: false + remarks: 文件ID + - column: + name: original_file_name + type: varchar(100) + constraints: + nullable: false + remarks: 文件名称 + - column: + name: file_purpose + type: varchar(20) + constraints: + nullable: false + remarks: 文件用途 + - column: + name: bucket_name + type: varchar(50) + constraints: + nullable: false + remarks: 储存桶名称 + - column: + name: object_name + type: varchar(50) + constraints: + nullable: false + remarks: 储存对象名称 + - column: + name: file_size + type: int8 + constraints: + nullable: false + remarks: 文件大小 + - column: + name: content_type + type: int8 + constraints: + nullable: false + remarks: 文件contentType + - column: + name: file_md5 + type: varchar(50) + constraints: + nullable: false + remarks: 文件MD5 + - column: + name: download_count + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 文件下载次数 + - column: + name: last_download_time + type: timestamp(6) + constraints: + nullable: true + remarks: 最后一次下载时间 + - column: + name: file_status + type: int2 + constraints: + nullable: false + remarks: 文件状态 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_file_modify_column.yml b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_file_modify_column.yml new file mode 100644 index 0000000..c3ea0f6 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405172001_file_modify_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_menu_add_column + author: linck + changes: + - modifyDataType: + schemaName: public + tableName: storage_file + columnName: content_type + newDataType: varchar(100) \ No newline at end of file diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405402001_file_add_column_default_value.yml b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405402001_file_add_column_default_value.yml new file mode 100644 index 0000000..615fec5 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/db/changelogs/202405402001_file_add_column_default_value.yml @@ -0,0 +1,9 @@ +databaseChangeLog: + - changeSet: + id: 202405402001_file_add_column_default_value.yml + author: linck + changes: + - addDefaultValue: + tableName: storage_file + columnName: file_purpose + defaultValue: "" diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/db/main.yml b/passiflora-server/passiflora-storage-app/src/main/resources/db/main.yml new file mode 100644 index 0000000..be0678f --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/db/main.yml @@ -0,0 +1,3 @@ +databaseChangeLog: + - includeAll: + path: src/main/resources/db/changelogs \ No newline at end of file diff --git a/passiflora-server/passiflora-storage-app/src/main/resources/log4j2-spring.xml b/passiflora-server/passiflora-storage-app/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..bfcceda --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/main/resources/log4j2-spring.xml @@ -0,0 +1,65 @@ + + + + + + + + + + ${spring:spring.application.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/passiflora-server/passiflora-storage-app/src/test/java/com/zerolinck/passiflora/storage/StorageFileControllerTest.java b/passiflora-server/passiflora-storage-app/src/test/java/com/zerolinck/passiflora/storage/StorageFileControllerTest.java new file mode 100644 index 0000000..588ca82 --- /dev/null +++ b/passiflora-server/passiflora-storage-app/src/test/java/com/zerolinck/passiflora/storage/StorageFileControllerTest.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.storage; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import cn.hutool.crypto.digest.DigestUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.storage.entity.StorageFile; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +/** + * 通用文件 Unit Test + * + * @author linck + * @since 2024-05-17 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class StorageFileControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testStorageFileId; + + private static String quickUploadStorageFileId; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/storageFile/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testUpload() throws Exception { + MockMultipartFile file = new MockMultipartFile( + "file", + "test.txt", + "text/plain", + "This is a test file".getBytes() + ); + mockMvc + .perform(multipart("/storageFile/upload").file(file)) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testStorageFileId = + objectMapper.convertValue(jsonNode.get("data"), String.class); + }); + } + + @Test + @Order(3) + public void testTryQuicklyUpload() throws Exception { + MockMultipartFile file = new MockMultipartFile( + "file", + "test.txt", + "text/plain", + "This is a test file".getBytes() + ); + StorageFile storageFile = new StorageFile(); + storageFile.setOriginalFileName("quickTest.txt"); + storageFile.setContentType("text/plain"); + storageFile.setFileMd5(DigestUtil.md5Hex(file.getBytes())); + mockMvc + .perform( + post("/storageFile/tryQuicklyUpload") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(storageFile)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + quickUploadStorageFileId = + objectMapper.convertValue(jsonNode.get("data"), String.class); + }); + } + + @Test + @Order(4) + public void testListByFileIds() throws Exception { + mockMvc + .perform( + post("/storageFile/listByFileIds") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { + testStorageFileId, + quickUploadStorageFileId, + } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(5) + public void testDetail() throws Exception { + mockMvc + .perform( + get("/storageFile/detail").param("fileId", testStorageFileId) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(6) + public void testConfirmFile() throws Exception { + mockMvc + .perform( + post("/storageFile/confirmFile") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testStorageFileId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(7) + public void testDownloadFile() throws Exception { + MvcResult result = mockMvc + .perform( + get("/storageFile/downloadFile") + .param("fileId", testStorageFileId) + ) + .andExpect(status().isOk()) + .andReturn(); + MockHttpServletResponse response = result.getResponse(); + assertNotNull(response.getContentAsByteArray()); + } + + @Test + @Order(8) + public void testDownloadZip() throws Exception { + MvcResult result = mockMvc + .perform( + post("/storageFile/downloadZip") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { + testStorageFileId, + quickUploadStorageFileId, + } + ) + ) + ) + .andExpect(status().isOk()) + .andReturn(); + MockHttpServletResponse response = result.getResponse(); + assertNotNull(response.getContentAsByteArray()); + } + + @Test + @Order(9) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/storageFile/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { + testStorageFileId, + quickUploadStorageFileId, + } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/build.gradle b/passiflora-server/passiflora-system-app/build.gradle new file mode 100644 index 0000000..88eeeea --- /dev/null +++ b/passiflora-server/passiflora-system-app/build.gradle @@ -0,0 +1,109 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + id 'java' + id 'jacoco' + id 'org.liquibase.gradle' version '2.2.1' + id 'org.springframework.boot' version '3.3.0' +} + +configurations { + all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' +} + +ext { + // 默认是开发环境;切换生产环境使用 gradle -Denv=prod + environment = System.getProperty("env", "dev") + print('使用 ' + environment + ' 环境编译') + configFile = file('../config.groovy') + configMap = new ConfigSlurper(environment).parse(configFile.toURI().toURL()).toProperties() + configMap['projectVersion'] = project.version + configMap['env'] = environment + configMap['isProd'] = (environment == 'prod') ? 'true' : 'false' + configMap['isDev'] = (environment == 'dev') ? 'true' : 'false' +} + +dependencies { + implementation project(':modules:passiflora-feign') + annotationProcessor platform(project(':modules:passiflora-bom')) + testAnnotationProcessor platform(project(':modules:passiflora-bom')) + liquibaseRuntime platform(project(':modules:passiflora-bom')) + + annotationProcessor 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.mapstruct:mapstruct-processor' + testAnnotationProcessor 'org.mapstruct:mapstruct-processor' + annotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + testAnnotationProcessor 'org.projectlombok:lombok-mapstruct-binding' + + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // 非开发环境,排除 Swagger UI + if (configMap['isDev'] == 'true') { + implementation 'com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter' + } else { + implementation('com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter') { + exclude group: 'com.github.xiaoymin', module: 'knife4j-openapi3-ui' + exclude group: 'org.webjars', module: 'swagger-ui' + } + } + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery' + implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer' + + implementation 'org.redisson:redisson-spring-boot-starter' + implementation 'com.baomidou:mybatis-plus-boot-starter' + implementation 'cn.hutool:hutool-crypto' + implementation 'org.postgresql:postgresql' + + // liquibase + liquibaseRuntime 'org.liquibase:liquibase-core' + liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl' + liquibaseRuntime 'info.picocli:picocli' + liquibaseRuntime 'org.postgresql:postgresql' +} + +test { + useJUnitPlatform() +} + +compileJava { + doLast { + copy { + from("src/main/java") { + include("**/*Mapper.xml") + } + into("build/classes/java/main") + } + } +} + +jar { + enabled = false +} + +processResources { + // src/main/resources 下的文件中 @key@ 的内容使用 config.groovy 里对应的进行替换 + from('src/main/resources') { + filter(ReplaceTokens, tokens: configMap) + duplicatesStrategy(DuplicatesStrategy.INCLUDE) + } + exclude 'db/**' +} + +liquibase { + activities { + main { + changelogFile 'src/main/resources/db/main.yml' + url configMap.'system_app.database.url' + username configMap.'system_app.database.username' + password configMap.'system_app.database.username' + logLevel "info" + } + } +} \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/PassifloraSystemApplication.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/PassifloraSystemApplication.java new file mode 100644 index 0000000..61ce63c --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/PassifloraSystemApplication.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.util.NetUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +@Slf4j +@SpringBootApplication +@MapperScan("com.zerolinck.**.mapper") +@EnableConfigurationProperties(PassifloraProperties.class) +@EnableFeignClients(basePackages = { "com.zerolinck.passiflora.feign.**" }) +public class PassifloraSystemApplication { + + @SneakyThrows + public static void main(String[] args) { + ConfigurableApplicationContext application = SpringApplication.run( + PassifloraSystemApplication.class, + args + ); + ConfigurableEnvironment environment = application.getEnvironment(); + String env = environment.getProperty("passiflora.env"); + String projectVersion = environment.getProperty( + "passiflora.project-version" + ); + String port = environment.getProperty("server.port"); + String path = environment.getProperty("server.servlet.context-path"); + String outIp = NetUtil.findOutIp(); + log.info( + """ + \n项目启动成功: {} 环境 ◉‿◉ + 本地 Swagger: \t\thttp://localhost:{}{}/doc.html + 外部地址 Swagger: \thttp://{}:{}{}/doc.html + Passiflora (C) 2024 version: {}""", + env, + port, + path, + outIp, + port, + path, + projectVersion + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/aop/UpdateAspect.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/aop/UpdateAspect.java new file mode 100644 index 0000000..92cc0be --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/aop/UpdateAspect.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.aop; + +import com.zerolinck.passiflora.model.common.BaseEntity; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +/** + * 拦截 update 方法,置空 createAt 和 createBy + * + * @author linck + * @since 2024-03-19 + */ +@Slf4j +@Aspect +@Component +public class UpdateAspect { + + @Before( + "execution(*" + + " com.zerolinck.passiflora.system.*.*Controller.update(com.zerolinck.passiflora.model.common.BaseEntity+))" + + " && args(entity)" + ) + public void beforeUpdate(JoinPoint joinPoint, BaseEntity entity) { + if (log.isDebugEnabled()) { + log.debug("UpdateAspect 前置处理,置空 createAt 和 createBy"); + } + entity.setCreateTime(null); + entity.setCreateBy(null); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/EnumController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/EnumController.java new file mode 100644 index 0000000..ccd492b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/EnumController.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import cn.hutool.core.util.ClassUtil; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.feign.system.EnumApi; +import com.zerolinck.passiflora.model.common.LabelValueInterface; +import jakarta.annotation.PostConstruct; +import java.lang.reflect.Method; +import java.util.*; +import lombok.SneakyThrows; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + */ +@RestController +@RequestMapping("enum") +public class EnumController implements EnumApi { + + private final Map>> MAP = new HashMap<>(); + + @SneakyThrows + @PostConstruct + public void init() { + initEnum(LabelValueInterface.class); + } + + @Override + public Result>> get(String enumName) { + return Result.ok(MAP.get(enumName)); + } + + @SneakyThrows + private void initEnum(Class c) { + Set> classes1 = ClassUtil.scanPackage( + "com.zerolinck", + aClass -> c.isAssignableFrom(aClass) && !c.equals(aClass) + ); + + for (Class clazz : classes1) { + Object[] enumConstants = clazz.getEnumConstants(); + // 遍历枚举实例 + for (Object enumInstance : enumConstants) { + Method getNameMethod = clazz.getMethod("getLabel"); + Object name = getNameMethod.invoke(enumInstance); + Method getValueMethod = clazz.getMethod("getValue"); + Object value = getValueMethod.invoke(enumInstance); + if (!MAP.containsKey(clazz.getSimpleName())) { + MAP.put(clazz.getSimpleName(), new ArrayList<>()); + } + MAP + .get(clazz.getSimpleName()) + .add(Map.of("label", name.toString(), "value", value)); + } + } + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictController.java new file mode 100644 index 0000000..acceb2b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictController.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysDictApi; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import com.zerolinck.passiflora.system.service.SysDictService; +import jakarta.annotation.Resource; +import java.util.List; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-04-01 + */ +@RestController +@RequestMapping("sysDict") +public class SysDictController implements SysDictApi { + + @Resource + private SysDictService sysDictService; + + @Override + public Result> page( + QueryCondition condition + ) { + return Result.page(sysDictService.page(condition)); + } + + @Override + public Result add(SysDict sysDict) { + sysDict.setDictId(null); + sysDictService.add(sysDict); + return Result.ok(sysDict.getDictId()); + } + + @Override + public Result update(SysDict sysDict) { + boolean success = sysDictService.update(sysDict); + if (success) { + return Result.ok(sysDict.getDictId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail(String dictId) { + AssertUtil.notBlank(dictId); + return Result.ok(sysDictService.detail(dictId)); + } + + @Override + public Result delete(List dictIds) { + AssertUtil.notEmpty(dictIds); + sysDictService.deleteByIds(dictIds); + return Result.ok(); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictItemController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictItemController.java new file mode 100644 index 0000000..c1be83c --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysDictItemController.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysDictItemApi; +import com.zerolinck.passiflora.model.system.entity.SysDictItem; +import com.zerolinck.passiflora.system.service.SysDictItemService; +import jakarta.annotation.Resource; +import java.util.List; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-04-01 + */ +@RestController +@RequestMapping("sysDictItem") +public class SysDictItemController implements SysDictItemApi { + + @Resource + private SysDictItemService sysDictItemService; + + @Override + public Result> page( + QueryCondition condition + ) { + return Result.page(sysDictItemService.page(condition)); + } + + @Override + public Result add(SysDictItem sysDictItem) { + sysDictItem.setDictItemId(null); + sysDictItemService.add(sysDictItem); + return Result.ok(sysDictItem.getDictItemId()); + } + + @Override + public Result update(SysDictItem sysDictItem) { + boolean success = sysDictItemService.update(sysDictItem); + if (success) { + return Result.ok(sysDictItem.getDictItemId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail(String dictItemId) { + AssertUtil.notBlank(dictItemId); + return Result.ok(sysDictItemService.detail(dictItemId)); + } + + @Override + public Result delete(List dictItemIds) { + AssertUtil.notEmpty(dictItemIds); + sysDictItemService.deleteByIds(dictItemIds); + return Result.ok(); + } + + @Override + public Result> listByDictId(String dictId) { + AssertUtil.notBlank(dictId); + return Result.ok(sysDictItemService.listByDictId(dictId)); + } + + @Override + public Result> listByDictName(String dictName) { + AssertUtil.notBlank(dictName); + return Result.ok(sysDictItemService.listByDictName(dictName)); + } + + @Override + public Result> listByDictTag(String dictTag) { + AssertUtil.notBlank(dictTag); + return Result.ok(sysDictItemService.listByDictTag(dictTag)); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysOrgController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysOrgController.java new file mode 100644 index 0000000..e0b0e89 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysOrgController.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysOrgApi; +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import com.zerolinck.passiflora.model.system.vo.SysOrgVo; +import com.zerolinck.passiflora.system.service.SysOrgService; +import jakarta.annotation.Resource; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-04-09 + */ +@Slf4j +@RestController +@RequestMapping("sysOrg") +public class SysOrgController implements SysOrgApi { + + @Resource + private SysOrgService sysOrgService; + + @Override + public Result> page(QueryCondition condition) { + return Result.page(sysOrgService.page(condition)); + } + + @Override + public Result add(SysOrg sysOrg) { + sysOrg.setOrgId(IdWorker.getIdStr()); + if (StrUtil.isBlank(sysOrg.getParentOrgId())) { + sysOrg.setParentOrgId("0"); + } + sysOrgService.add(sysOrg); + return Result.ok(sysOrg.getOrgId()); + } + + @Override + public Result update(SysOrg sysOrg) { + if (StrUtil.isBlank(sysOrg.getParentOrgId())) { + sysOrg.setParentOrgId("0"); + } + boolean success = sysOrgService.update(sysOrg); + if (success) { + return Result.ok(sysOrg.getOrgId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail(String orgId) { + AssertUtil.notBlank(orgId); + return Result.ok(sysOrgService.detail(orgId)); + } + + /** + * 此方法会级联删除下级机构 + */ + @Override + public Result delete(List orgIds) { + AssertUtil.notEmpty(orgIds); + sysOrgService.deleteByIds(orgIds); + return Result.ok(); + } + + @Override + public Result> listByParentId(String orgParentId) { + if (StrUtil.isBlank(orgParentId)) { + orgParentId = "0"; + } + return Result.ok(sysOrgService.listByParentId(orgParentId)); + } + + @Override + public Result> orgTree() { + return Result.ok(sysOrgService.orgTree()); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPermissionController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPermissionController.java new file mode 100644 index 0000000..7be378b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPermissionController.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysPermissionApi; +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import com.zerolinck.passiflora.model.system.vo.SysPermissionVo; +import com.zerolinck.passiflora.system.service.SysPermissionService; +import jakarta.annotation.Resource; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-05-06 + */ +@Slf4j +@RestController +@RequestMapping("sysPermission") +public class SysPermissionController implements SysPermissionApi { + + @Resource + private SysPermissionService sysPermissionService; + + @Override + public Result> page( + QueryCondition condition + ) { + return Result.page(sysPermissionService.page(condition)); + } + + @Override + public Result add(SysPermission sysPermission) { + sysPermission.setPermissionId(IdWorker.getIdStr()); + if (StrUtil.isBlank(sysPermission.getPermissionParentId())) { + sysPermission.setPermissionParentId("0"); + } + sysPermissionService.add(sysPermission); + return Result.ok(sysPermission.getPermissionId()); + } + + @Override + public Result update(SysPermission sysPermission) { + boolean success = sysPermissionService.update(sysPermission); + if (StrUtil.isBlank(sysPermission.getPermissionParentId())) { + sysPermission.setPermissionParentId("0"); + } + if (success) { + return Result.ok(sysPermission.getPermissionId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail(String permissionId) { + AssertUtil.notBlank(permissionId); + return Result.ok(sysPermissionService.detail(permissionId)); + } + + @Override + public Result delete(List permissionIds) { + AssertUtil.notEmpty(permissionIds); + sysPermissionService.deleteByIds(permissionIds); + return Result.ok(); + } + + @Override + public Result> menuTree() { + return Result.ok(sysPermissionService.menuTree()); + } + + @Override + public Result> permissionTableTree() { + return Result.ok(sysPermissionService.permissionTableTree()); + } + + @Override + public Result updateOrder( + List sysPermissionTableVos + ) { + AssertUtil.notEmpty(sysPermissionTableVos); + sysPermissionService.updateOrder(sysPermissionTableVos); + return Result.ok(); + } + + @Override + public Result disable(List permissionIds) { + sysPermissionService.disable(permissionIds); + return Result.ok(); + } + + @Override + public Result enable(List permissionIds) { + sysPermissionService.enable(permissionIds); + return Result.ok(); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPositionController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPositionController.java new file mode 100644 index 0000000..4d70108 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysPositionController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysPositionApi; +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import com.zerolinck.passiflora.model.system.vo.SysPositionVo; +import com.zerolinck.passiflora.system.service.SysPositionService; +import jakarta.annotation.Resource; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-05-14 + */ +@Slf4j +@RestController +@RequestMapping("sysPosition") +public class SysPositionController implements SysPositionApi { + + @Resource + private SysPositionService sysPositionService; + + @Override + public Result> page( + QueryCondition condition + ) { + return Result.page(sysPositionService.page(condition)); + } + + @Override + public Result add(SysPosition sysPosition) { + sysPosition.setPositionId(IdWorker.getIdStr()); + if (StrUtil.isBlank(sysPosition.getParentPositionId())) { + sysPosition.setParentPositionId("0"); + } + sysPositionService.add(sysPosition); + return Result.ok(sysPosition.getPositionId()); + } + + @Override + public Result update(SysPosition sysPosition) { + if (StrUtil.isBlank(sysPosition.getParentPositionId())) { + sysPosition.setParentPositionId("0"); + } + boolean success = sysPositionService.update(sysPosition); + if (success) { + return Result.ok(sysPosition.getPositionId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail(String positionId) { + AssertUtil.notBlank(positionId); + return Result.ok(sysPositionService.detail(positionId)); + } + + @Override + public Result delete(List positionIds) { + AssertUtil.notEmpty(positionIds); + sysPositionService.deleteByIds(positionIds); + return Result.ok(); + } + + @Override + public Result> positionTree() { + return Result.ok(sysPositionService.positionTree()); + } + + @Override + public Result disable(List positionIds) { + sysPositionService.disable(positionIds); + return Result.ok(); + } + + @Override + public Result enable(List positionIds) { + sysPositionService.enable(positionIds); + return Result.ok(); + } + + @Override + public Result updateOrder(List sysPositionVos) { + AssertUtil.notEmpty(sysPositionVos); + sysPositionService.updateOrder(sysPositionVos); + return Result.ok(); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysUserController.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysUserController.java new file mode 100644 index 0000000..5b1e0e7 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/controller/SysUserController.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.controller; + +import com.zerolinck.passiflora.common.api.ListWithPage; +import com.zerolinck.passiflora.common.api.Result; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.common.util.AssertUtil; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.feign.system.SysUserApi; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import com.zerolinck.passiflora.model.system.mapperstruct.SysUserConvert; +import com.zerolinck.passiflora.model.system.valid.Login; +import com.zerolinck.passiflora.model.system.vo.SysUserInfo; +import com.zerolinck.passiflora.model.system.vo.SysUserVo; +import com.zerolinck.passiflora.model.valid.Insert; +import com.zerolinck.passiflora.model.valid.Update; +import com.zerolinck.passiflora.system.service.SysUserService; +import jakarta.annotation.Resource; +import java.util.List; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author linck + * @since 2024-02-05 + */ +@RestController +@RequestMapping("sysUser") +public class SysUserController implements SysUserApi { + + @Resource + private SysUserService sysUserService; + + @Override + public Result> page( + String orgId, + QueryCondition condition + ) { + return Result.page(sysUserService.page(orgId, condition)); + } + + @Override + public Result add( + @RequestBody @Validated(Insert.class) SysUser sysUser + ) { + sysUser.setUserId(null); + sysUserService.add(sysUser); + return Result.ok(sysUser.getUserId()); + } + + @Override + public Result update( + @RequestBody @Validated(Update.class) SysUser sysUser + ) { + sysUser.setUserPassword(null); + sysUser.setUserName(null); + sysUser.setSalt(null); + boolean success = sysUserService.update(sysUser); + if (success) { + return Result.ok(sysUser.getUserId()); + } else { + return Result.failed(ResultCodeEnum.COMPETE_FAILED); + } + } + + @Override + public Result detail( + @RequestParam(value = "userId") String userId + ) { + AssertUtil.notBlank(userId); + return Result.ok(sysUserService.detail(userId)); + } + + @Override + public Result delete(@RequestBody List userIds) { + AssertUtil.notEmpty(userIds); + sysUserService.deleteByIds(userIds); + return Result.ok(); + } + + @Override + public Result login( + @RequestBody @Validated(Login.class) SysUser sysUser + ) { + return Result.ok(sysUserService.login(sysUser)); + } + + @Override + public Result logout() { + sysUserService.logout(); + return Result.ok(); + } + + @Override + public Result currentUserInfo() { + SysUserInfo sysUserInfo = SysUserConvert.INSTANCE.entity2info( + sysUserService.getById(CurrentUtil.getCurrentUserId()) + ); + // FIXME mock data + sysUserInfo.getMenu().add("workplace"); + sysUserInfo.getMenu().add("user"); + sysUserInfo.getMenu().add("system"); + sysUserInfo.getMenu().add("dict"); + sysUserInfo.getMenu().add("organization"); + sysUserInfo.getMenu().add("org"); + sysUserInfo.getMenu().add("permission"); + sysUserInfo.getMenu().add("position"); + return Result.ok(sysUserInfo); + } + + @Override + public Result checkToken() { + return Result.ok(sysUserService.checkToken()); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictItemMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictItemMapper.java new file mode 100644 index 0000000..87bdabb --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictItemMapper.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysDictItem; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * @author linck + * @since 2024-04-01 + */ +public interface SysDictItemMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("dictItemIds") Collection dictItemIds, + @Param("updateBy") String updateBy + ); + + int deleteByDictIds( + @Param("dictIds") Collection dictIds, + @Param("updateBy") String updateBy + ); + + @Select( + "SELECT * FROM sys_dict_item WHERE del_flag = 0 AND dict_id = #{dictId}" + ) + List listByDictId(@Param("dictId") String dictId); + + @Select( + "SELECT * FROM sys_dict_item WHERE del_flag = 0 AND dict_id = (SELECT dict_id from" + + " sys_dict WHERE dict_name = #{dictName})" + ) + List listByDictName(@Param("dictName") String dictName); + + @Select( + "SELECT * FROM sys_dict_item WHERE del_flag = 0 AND dict_id = (SELECT dict_id from" + + " sys_dict WHERE dict_tag = #{dictTag})" + ) + List listByDictTag(@Param("dictTag") String dictTag); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictMapper.java new file mode 100644 index 0000000..bf66a01 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysDictMapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * @author linck + * @since 2024-04-01 + */ +public interface SysDictMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("dictIds") Collection dictIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysOrgMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysOrgMapper.java new file mode 100644 index 0000000..8b1bc5f --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysOrgMapper.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import com.zerolinck.passiflora.model.system.vo.SysOrgVo; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * @author linck + * @since 2024-04-09 + */ +public interface SysOrgMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** + * 使用更新删除,保证 update_by 和 update_time 正确 + */ + int deleteByIds( + @Param("orgIds") Collection orgIds, + @Param("updateBy") String updateBy + ); + + /** + * 此方法会级联删除下级机构 + */ + int deleteById( + @Param("orgId") String orgId, + @Param("updateBy") String updateBy + ); + + @Select( + "SELECT * FROM sys_org WHERE del_flag = 0 AND org_code = #{orgCode}" + ) + SysOrg selectByOrgCode(@Param("orgCode") String orgCode); + + @Select( + "SELECT * FROM sys_org WHERE del_flag = 0 AND parent_org_id = #{orgParentId} ORDER BY" + + " org_level, \"order\", org_name" + ) + List listByParentId(@Param("orgParentId") String orgParentId); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPermissionMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPermissionMapper.java new file mode 100644 index 0000000..f59ba3c --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPermissionMapper.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * @author linck + * @since 2024-05-06 + */ +public interface SysPermissionMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("permissionIds") Collection permissionIds, + @Param("updateBy") String updateBy + ); + + @Update( + "UPDATE sys_permission SET \"order\" = #{sysPermissionTableVo.order} WHERE" + + " permission_id = #{sysPermissionTableVo.permissionId} " + ) + void updateOrder( + @Param("sysPermissionTableVo") SysPermissionTableVo sysPermissionTableVo + ); + + void disable( + @Param("permissionIds") Collection permissionIds, + @Param("updateBy") String updateBy + ); + + void enable( + @Param("permissionIds") Collection permissionIds, + @Param("updateBy") String updateBy + ); + + @Select( + "SELECT * FROM sys_permission WHERE del_flag = 0 AND permission_parent_id = #{permissionParentId} ORDER BY" + + " permission_level , \"order\", permission_title" + ) + List listByParentId( + @Param("permissionParentId") String permissionParentId + ); + + @Select( + "SELECT * FROM sys_permission WHERE del_flag = 0 AND permission_id_path like concat('%', #{permissionId}, '%') ORDER BY" + + " permission_level , \"order\", permission_title" + ) + List listSelfAndSub( + @Param("permissionId") String permissionId + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionDataScopeMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionDataScopeMapper.java new file mode 100644 index 0000000..265884a --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionDataScopeMapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysPositionDataScope; +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * @author linck + * @since 2024-05-14 + */ +public interface SysPositionDataScopeMapper + extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper< + SysPositionDataScope + > searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("scopeIds") Collection scopeIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionMapper.java new file mode 100644 index 0000000..789d4d2 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionMapper.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import com.zerolinck.passiflora.model.system.vo.SysPositionVo; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * @author linck + * @since 2024-05-14 + */ +public interface SysPositionMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("positionIds") Collection positionIds, + @Param("updateBy") String updateBy + ); + + @Select( + "SELECT * FROM sys_position WHERE del_flag = 0 AND position_name = #{positionName}" + ) + SysPosition selectByPositionName( + @Param("positionName") String positionName + ); + + @Select( + "SELECT * FROM sys_position WHERE del_flag = 0 AND parent_position_id = #{positionParentId} ORDER BY" + + " position_level , \"order\", position_name" + ) + List listByParentId( + @Param("positionParentId") String positionParentId + ); + + void disable( + @Param("positionIds") Collection positionIds, + @Param("updateBy") String updateBy + ); + + void enable( + @Param("positionIds") Collection positionIds, + @Param("updateBy") String updateBy + ); + + @Update( + "UPDATE sys_position SET \"order\" = #{sysPositionVo.order} WHERE" + + " position_id = #{sysPositionVo.positionId} " + ) + void updateOrder(@Param("sysPositionVo") SysPositionVo sysPositionVo); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionPermissionMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionPermissionMapper.java new file mode 100644 index 0000000..e00487b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysPositionPermissionMapper.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysPositionPermission; +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * @author linck + * @since 2024-05-06 + */ +public interface SysPositionPermissionMapper + extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper< + SysPositionPermission + > searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("bindIds") Collection bindIds, + @Param("updateBy") String updateBy + ); + + int deleteByPositionIds( + @Param("positionIds") Collection positionIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..769c169 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserMapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * @author linck + * @since 2024-02-07 + */ +public interface SysUserMapper extends BaseMapper { + Page page( + @Param("orgId") String orgId, + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("userIds") Collection userIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserPositionMapper.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserPositionMapper.java new file mode 100644 index 0000000..449d9e0 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/SysUserPositionMapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zerolinck.passiflora.model.system.entity.SysUserPosition; +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * @author linck + * @since 2024-05-14 + */ +public interface SysUserPositionMapper extends BaseMapper { + Page page( + IPage page, + @Param(Constants.WRAPPER) QueryWrapper searchWrapper, + @Param("sortWrapper") QueryWrapper sortWrapper + ); + + /** 使用更新删除,保证 update_by 和 update_time 正确 */ + int deleteByIds( + @Param("bindIds") Collection bindIds, + @Param("updateBy") String updateBy + ); +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictItemMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictItemMapper.xml new file mode 100644 index 0000000..47d0354 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictItemMapper.xml @@ -0,0 +1,46 @@ + + + + + + + dict_item_id, dict_id, label, value, remark, is_system, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_dict_item SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE dict_item_id IN + + #{id} + + + + + UPDATE sys_dict_item SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE dict_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictMapper.xml new file mode 100644 index 0000000..45957a5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysDictMapper.xml @@ -0,0 +1,36 @@ + + + + + + + dict_id, dict_name, remark, is_system, value_is_only, dict_tag, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_dict SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE dict_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysOrgMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysOrgMapper.xml new file mode 100644 index 0000000..c0b641c --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysOrgMapper.xml @@ -0,0 +1,43 @@ + + + + + + + org_id, org_name, org_code, org_type, org_level, parent_org_id, org_id_path, create_by, update_by, create_time, update_time, del_flag, version, "order" + + + + + + UPDATE sys_org SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE org_id IN + + #{id} + + + + + + UPDATE sys_org SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE sys_org.org_id_path like #{path} + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPermissionMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPermissionMapper.xml new file mode 100644 index 0000000..1920ebe --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPermissionMapper.xml @@ -0,0 +1,55 @@ + + + + + + + permission_id, permission_title, permission_name, permission_icon, permission_parent_id, permission_id_path, "order", remark, create_by, update_by, create_time, update_time, del_flag, version, permission_level, permission_status, permission_type + + + + + + UPDATE sys_permission SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE permission_id IN + + #{id} + + + + + UPDATE sys_permission SET update_time = now(), + update_by = #{updateBy}, + permission_status = 0 + WHERE + + permission_id_path like CONCAT('%', #{id}, '%') + + + + + UPDATE sys_permission SET update_time = now(), + update_by = #{updateBy}, + permission_status = 1 + WHERE permission_id IN + + #{id} + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionDataScopeMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionDataScopeMapper.xml new file mode 100644 index 0000000..0874b88 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionDataScopeMapper.xml @@ -0,0 +1,36 @@ + + + + + + + scope_id, position_id, org_id, data_scope, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_position_data_scope SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE scope_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionMapper.xml new file mode 100644 index 0000000..38a0aa6 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionMapper.xml @@ -0,0 +1,55 @@ + + + + + + + position_id, position_name, position_level, parent_position_id, data_scope_type, position_id_path, create_by, update_by, create_time, update_time, del_flag, version, position_status, "order" + + + + + + UPDATE sys_position SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE position_id IN + + #{id} + + + + + UPDATE sys_position SET update_time = now(), + update_by = #{updateBy}, + position_status = 0 + WHERE + + position_id_path like CONCAT('%', #{id}, '%') + + + + + UPDATE sys_position SET update_time = now(), + update_by = #{updateBy}, + position_status = 1 + WHERE position_id IN + + #{id} + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionPermissionMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionPermissionMapper.xml new file mode 100644 index 0000000..957bff0 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysPositionPermissionMapper.xml @@ -0,0 +1,46 @@ + + + + + + + bind_id, position_id, permission_id, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_position_menu SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE bind_id IN + + #{id} + + + + + UPDATE sys_position_menu SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE position_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserMapper.xml new file mode 100644 index 0000000..0c87265 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserMapper.xml @@ -0,0 +1,40 @@ + + + + + + + user_id, user_name, real_name, org_id, id_card_no, date_of_birth, gender, phone_num, email, avatar_file, remark, user_password, salt, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_user SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE user_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserPositionMapper.xml b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserPositionMapper.xml new file mode 100644 index 0000000..d39f34d --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/mapper/xml/SysUserPositionMapper.xml @@ -0,0 +1,36 @@ + + + + + + + bind_id, user_id, position_id, create_by, update_by, create_time, update_time, del_flag, version + + + + + + UPDATE sys_user_position SET update_time = now(), + update_by = #{updateBy}, + del_flag = 1 + WHERE bind_id IN + + #{id} + + + + \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictItemService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictItemService.java new file mode 100644 index 0000000..a7c6833 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictItemService.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.common.enums.YesOrNoEnum; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import com.zerolinck.passiflora.model.system.entity.SysDictItem; +import com.zerolinck.passiflora.system.mapper.SysDictItemMapper; +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-04-01 + */ +@Slf4j +@Service +public class SysDictItemService + extends ServiceImpl { + + @Resource + private SysDictService sysDictService; + + private static final String LOCK_KEY = "passiflora:lock:sysDictItem:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysDictItem.class), + condition.sortWrapper(SysDictItem.class) + ); + } + + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public void add(SysDictItem sysDictItem) { + SysDict sysDict = sysDictService.detail(sysDictItem.getDictId()); + + LockWrapper lockWrapper = new LockWrapper() + .lock(SysDictItem::getLabel, sysDictItem.getLabel()); + if (YesOrNoEnum.NO.equals(sysDict.getValueIsOnly())) { + lockWrapper.lock(SysDictItem::getValue, sysDictItem.getValue()); + } + + LockUtil.lockAndTransactionalLogic( + LOCK_KEY + sysDict.getDictTag(), + lockWrapper, + () -> { + Long count = baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysDictItem::getLabel, sysDictItem.getLabel()) + .eq(SysDictItem::getDictId, sysDictItem.getDictId()) + ); + if (count > 0) { + throw new BizException("字典项标签重复,请重新填写"); + } + + SysDict dict = sysDictService.getById(sysDictItem.getDictId()); + if (YesOrNoEnum.NO.equals(dict.getValueIsOnly())) { + count = + baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysDictItem::getValue, sysDictItem.getValue()) + .eq(SysDictItem::getDictId, sysDictItem.getDictId()) + ); + if (count > 0) { + throw new BizException("字典项标签重复,请重新填写"); + } + } + + baseMapper.insert(sysDictItem); + return null; + } + ); + } + + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public boolean update(SysDictItem sysDictItem) { + SysDict sysDict = sysDictService.detail(sysDictItem.getDictId()); + + LockWrapper lockWrapper = new LockWrapper() + .lock(SysDictItem::getLabel, sysDictItem.getLabel()); + if (YesOrNoEnum.NO.equals(sysDict.getValueIsOnly())) { + lockWrapper.lock(SysDictItem::getValue, sysDictItem.getValue()); + } + + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY + sysDict.getDictTag(), + lockWrapper, + () -> { + SysDictItem dbSysDictItem = baseMapper.selectById( + sysDictItem.getDictItemId() + ); + if (YesOrNoEnum.YES.equals(sysDictItem.getIsSystem())) { + throw new BizException("系统内置数据,不允许修改"); + } + + if ( + sysDictItem.getLabel() != null && + !sysDictItem.getLabel().equals(dbSysDictItem.getLabel()) + ) { + Long count = baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysDictItem::getLabel, sysDictItem.getLabel()) + .eq(SysDictItem::getDictId, sysDictItem.getDictId()) + .ne(SysDictItem::getDictId, sysDictItem.getDictId()) + ); + if (count > 0) { + throw new BizException("字典值名称重复,请重新填写"); + } + } + + SysDict dict = sysDictService.getById(sysDictItem.getDictId()); + if (YesOrNoEnum.NO.equals(dict.getValueIsOnly())) { + Long count = baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysDictItem::getValue, sysDictItem.getValue()) + .eq(SysDictItem::getDictId, sysDictItem.getDictId()) + .ne( + SysDictItem::getDictItemId, + sysDictItem.getDictItemId() + ) + ); + if (count > 0) { + throw new BizException("字典项标签重复,请重新填写"); + } + } + + int changeRowCount = baseMapper.updateById(sysDictItem); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public int deleteByIds(Collection dictItemIds) { + List sysDictItems = baseMapper.selectBatchIds(dictItemIds); + sysDictItems.forEach(sysDictItem -> { + if (YesOrNoEnum.YES.equals(sysDictItem.getIsSystem())) { + throw new BizException("系统内置数据,不允许删除"); + } + }); + return baseMapper.deleteByIds( + dictItemIds, + CurrentUtil.getCurrentUserId() + ); + } + + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public void deleteByDictIds(Collection dictIds) { + baseMapper.deleteByDictIds(dictIds, CurrentUtil.getCurrentUserId()); + } + + public SysDictItem detail(String dictItemId) { + SysDictItem sysDictItem = baseMapper.selectById(dictItemId); + if (sysDictItem == null) { + throw new BizException("无对应字典项数据,请刷新后重试"); + } + return sysDictItem; + } + + @Cacheable(value = "passiflora:dict:id", key = "#dictId") + public List listByDictId(String dictId) { + return baseMapper.listByDictId(dictId); + } + + @Cacheable(value = "passiflora:dict:name", key = "#dictName") + public List listByDictName(String dictName) { + return baseMapper.listByDictName(dictName); + } + + @Cacheable(value = "passiflora:dict:tag", key = "#dictTag") + public List listByDictTag(String dictTag) { + return baseMapper.listByDictTag(dictTag); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictService.java new file mode 100644 index 0000000..e11a50d --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysDictService.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.common.enums.YesOrNoEnum; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import com.zerolinck.passiflora.system.mapper.SysDictMapper; +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-04-01 + */ +@Slf4j +@Service +public class SysDictService extends ServiceImpl { + + @Lazy + @Resource + private SysDictItemService sysDictItemService; + + private static final String LOCK_KEY = "passiflora:lock:sysDict:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysDict.class), + condition.sortWrapper(SysDict.class) + ); + } + + public void add(SysDict sysDict) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysDict::getDictName, sysDict.getDictName()) + .lock(SysDict::getDictTag, sysDict.getDictTag()), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysDict); + baseMapper.insert(sysDict); + return null; + } + ); + } + + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public boolean update(SysDict sysDict) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysDict::getDictName, sysDict.getDictName()) + .lock(SysDict::getDictTag, sysDict.getDictTag()), + () -> { + SysDict dbSysDict = baseMapper.selectById(sysDict.getDictId()); + if (YesOrNoEnum.YES.equals(dbSysDict.getIsSystem())) { + throw new BizException("系统内置数据,不允许修改"); + } + + OnlyFieldCheck.checkUpdate(baseMapper, sysDict); + int changeRowCount = baseMapper.updateById(sysDict); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = "passiflora:dict", allEntries = true) + public int deleteByIds(Collection dictIds) { + List sysDicts = baseMapper.selectBatchIds(dictIds); + sysDicts.forEach(sysDict -> { + if (YesOrNoEnum.YES.equals(sysDict.getIsSystem())) { + throw new BizException("系统内置数据,不允许删除"); + } + }); + int rowCount = baseMapper.deleteByIds( + dictIds, + CurrentUtil.getCurrentUserId() + ); + sysDictItemService.deleteByDictIds(dictIds); + return rowCount; + } + + public SysDict detail(String dictId) { + SysDict sysDict = baseMapper.selectById(dictId); + if (sysDict == null) { + throw new BizException("无对应字典数据,请刷新后重试"); + } + return sysDict; + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysOrgService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysOrgService.java new file mode 100644 index 0000000..d1fe0f2 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysOrgService.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import com.zerolinck.passiflora.model.system.vo.SysOrgVo; +import com.zerolinck.passiflora.system.mapper.SysOrgMapper; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-04-09 + */ +@Slf4j +@Service +public class SysOrgService extends ServiceImpl { + + private static final String LOCK_KEY = "passiflora:lock:sysOrg:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysOrg.class), + condition.sortWrapper(SysOrg.class) + ); + } + + public void add(SysOrg sysOrg) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysOrg::getOrgName, sysOrg.getOrgName()) + .lock(SysOrg::getOrgCode, sysOrg.getOrgCode()), + () -> { + // 同一父机构下,机构名称不能重复 + Long count = baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysOrg::getOrgName, sysOrg.getOrgName()) + .eq(SysOrg::getParentOrgId, sysOrg.getParentOrgId()) + ); + if (count > 0) { + throw new BizException("机构名称重复,请重新填写"); + } + generateIadPathAndLevel(sysOrg); + baseMapper.insert(sysOrg); + return null; + } + ); + } + + public boolean update(SysOrg sysOrg) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysOrg::getOrgName, sysOrg.getOrgName()) + .lock(SysOrg::getOrgCode, sysOrg.getOrgCode()), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysOrg); + + // 同一父机构下,机构名称不能重复 + SysOrg dbSysOrg = baseMapper.selectById(sysOrg.getOrgId()); + if ( + sysOrg.getOrgName() != null && + !dbSysOrg.getOrgName().equals(sysOrg.getOrgName()) + ) { + Long count = baseMapper.selectCount( + new LambdaQueryWrapper() + .eq(SysOrg::getOrgName, sysOrg.getOrgName()) + .eq(SysOrg::getParentOrgId, sysOrg.getParentOrgId()) + .ne(SysOrg::getOrgId, sysOrg.getOrgId()) + ); + if (count > 0) { + throw new BizException("机构名称重复,请重新填写"); + } + } + generateIadPathAndLevel(sysOrg); + int changeRowCount = baseMapper.updateById(sysOrg); + // 子机构数据变更 + List sysOrgList = listByParentId(sysOrg.getOrgId()); + sysOrgList.forEach(org -> { + generateIadPathAndLevel(org); + baseMapper.updateById(org); + }); + return changeRowCount > 0; + } + ); + } + + /** 此方法会级联删除下级机构 */ + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection orgIds) { + int rowCount = 0; + for (String orgId : orgIds) { + rowCount += + baseMapper.deleteById(orgId, CurrentUtil.getCurrentUserId()); + } + return rowCount; + } + + public SysOrg detail(String orgId) { + SysOrg sysOrg = baseMapper.selectById(orgId); + if (sysOrg == null) { + throw new BizException("无对应机构数据,请刷新后重试"); + } + return sysOrg; + } + + public Map orgId2NameMap(List orgIds) { + if (CollectionUtil.isEmpty(orgIds)) { + return new HashMap<>(); + } + return baseMapper + .selectList( + new LambdaQueryWrapper().in(SysOrg::getOrgId, orgIds) + ) + .stream() + .collect(Collectors.toMap(SysOrg::getOrgId, SysOrg::getOrgName)); + } + + public SysOrg selectByOrgCode(String orgCode) { + return baseMapper.selectByOrgCode(orgCode); + } + + public List listByParentId(String orgParentId) { + return baseMapper.listByParentId(orgParentId); + } + + public List orgTree() { + List sysOrgVos = baseMapper.listByParentId("0"); + sysOrgVos.forEach(this::recursionTree); + return sysOrgVos; + } + + private void recursionTree(SysOrgVo sysOrgVo) { + sysOrgVo.setChildren(listByParentId(sysOrgVo.getOrgId())); + sysOrgVo.getChildren().forEach(this::recursionTree); + } + + private void generateIadPathAndLevel(SysOrg sysOrg) { + StringBuilder codeBuffer = new StringBuilder(); + String orgParentId = sysOrg.getParentOrgId(); + int level = 0; + if (!"0".equals(orgParentId)) { + SysOrg parentOrg = detail(orgParentId); + codeBuffer.append(parentOrg.getOrgIdPath()).append("/"); + level = parentOrg.getOrgLevel() + 1; + } + codeBuffer.append(sysOrg.getOrgId()); + sysOrg.setOrgLevel(level); + sysOrg.setOrgIdPath(codeBuffer.toString()); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPermissionService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPermissionService.java new file mode 100644 index 0000000..51485f5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPermissionService.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.common.enums.StatusEnum; +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.enums.PermissionTypeEnum; +import com.zerolinck.passiflora.model.system.mapperstruct.SysPermissionConvert; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import com.zerolinck.passiflora.model.system.vo.SysPermissionVo; +import com.zerolinck.passiflora.system.mapper.SysPermissionMapper; +import java.util.*; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-05-06 + */ +@Slf4j +@Service +public class SysPermissionService + extends ServiceImpl { + + private static final String LOCK_KEY = "passiflora:lock:sysPermission:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysPermission.class), + condition.sortWrapper(SysPermission.class) + ); + } + + public void add(SysPermission sysPermission) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock( + SysPermission::getPermissionTitle, + sysPermission.getPermissionTitle() + ), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysPermission); + generateIadPathAndLevel(sysPermission); + baseMapper.insert(sysPermission); + return null; + } + ); + } + + public boolean update(SysPermission sysPermission) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock( + SysPermission::getPermissionTitle, + sysPermission.getPermissionTitle() + ), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysPermission); + generateIadPathAndLevel(sysPermission); + int changeRowCount = baseMapper.updateById(sysPermission); + // 子权限数据变更 + List permissionList = listByParentId( + sysPermission.getPermissionId() + ); + permissionList.forEach(permission -> { + generateIadPathAndLevel(permission); + baseMapper.updateById(permission); + }); + return changeRowCount > 0; + } + ); + } + + public List listByParentId(String permissionParentId) { + return baseMapper.listByParentId(permissionParentId); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection permissionIds) { + return baseMapper.deleteByIds( + permissionIds, + CurrentUtil.getCurrentUserId() + ); + } + + public SysPermission detail(String permissionId) { + SysPermission sysPermission = baseMapper.selectById(permissionId); + if (sysPermission == null) { + throw new BizException("无对应菜单数据,请刷新后重试"); + } + return sysPermission; + } + + public List menuTree() { + // TODO 查询当前用户有权限的菜单 + Map> menuMap = baseMapper + .selectList( + new LambdaQueryWrapper() + .eq(SysPermission::getPermissionStatus, StatusEnum.ENABLE) + .in( + SysPermission::getPermissionType, + List.of( + PermissionTypeEnum.MENU_SET, + PermissionTypeEnum.MENU + ) + ) + .orderByAsc(SysPermission::getPermissionLevel) + .orderByAsc(SysPermission::getOrder) + ) + .stream() + .map(SysPermissionConvert.INSTANCE::entity2vo) + .collect( + Collectors.groupingBy( + SysPermissionVo::getPermissionParentId, + Collectors.toList() + ) + ); + // 顶层菜单 + List topMenu = menuMap.get("0"); + if (CollectionUtil.isEmpty(topMenu)) { + return new ArrayList<>(); + } + dealMenuTree(topMenu, menuMap); + return topMenu; + } + + public List permissionTableTree() { + // TODO 查询当前用户有权限的菜单 + Map> menuMap = baseMapper + .selectList( + new LambdaQueryWrapper() + .in( + SysPermission::getPermissionType, + List.of( + PermissionTypeEnum.MENU_SET, + PermissionTypeEnum.MENU + ) + ) + .orderByAsc(SysPermission::getPermissionLevel) + .orderByAsc(SysPermission::getOrder) + ) + .stream() + .map(SysPermissionConvert.INSTANCE::entity2tableVo) + .collect( + Collectors.groupingBy( + com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo::getPermissionParentId, + Collectors.toList() + ) + ); + // 顶层菜单 + List topMenu = menuMap.get("0"); + if (CollectionUtil.isEmpty(topMenu)) { + return new ArrayList<>(); + } + permissionTableTree(topMenu, menuMap); + return topMenu; + } + + public void updateOrder(List sysPermissionTableVos) { + if (CollectionUtil.isEmpty(sysPermissionTableVos)) { + return; + } + for (SysPermissionTableVo sysPermissionTableVo : sysPermissionTableVos) { + baseMapper.updateOrder(sysPermissionTableVo); + updateOrder(sysPermissionTableVo.getChildren()); + } + } + + @Transactional(rollbackFor = Exception.class) + public void disable(List permissionIds) { + baseMapper.disable(permissionIds, CurrentUtil.getCurrentUserId()); + } + + @Transactional(rollbackFor = Exception.class) + public void enable(List permissionIds) { + List pathIds = new ArrayList<>(); + permissionIds.forEach(permissionId -> { + SysPermission sysPermission = baseMapper.selectById(permissionId); + String[] permissionIdList = sysPermission + .getPermissionIdPath() + .split("/"); + Collections.addAll(pathIds, permissionIdList); + }); + baseMapper.enable(pathIds, CurrentUtil.getCurrentUserId()); + } + + List listSelfAndSub(String permissionId) { + return baseMapper.listSelfAndSub(permissionId); + } + + private void dealMenuTree( + List menuVos, + Map> menuMap + ) { + for (SysPermissionVo menu : menuVos) { + if (menuMap.containsKey(menu.getPermissionId())) { + menu.setChildren(menuMap.get(menu.getPermissionId())); + dealMenuTree(menu.getChildren(), menuMap); + } + } + } + + private void permissionTableTree( + List menuVos, + Map> menuMap + ) { + for (SysPermissionTableVo menu : menuVos) { + if (menuMap.containsKey(menu.getPermissionId())) { + menu.setChildren(menuMap.get(menu.getPermissionId())); + permissionTableTree(menu.getChildren(), menuMap); + } + } + } + + private void generateIadPathAndLevel(SysPermission sysPermission) { + StringBuilder codeBuffer = new StringBuilder(); + String permissionParentId = sysPermission.getPermissionParentId(); + int level = 1; + if (!"0".equals(permissionParentId)) { + SysPermission parentSysPermission = detail(permissionParentId); + codeBuffer + .append(parentSysPermission.getPermissionIdPath()) + .append("/"); + level = parentSysPermission.getPermissionLevel() + 1; + } + codeBuffer.append(sysPermission.getPermissionId()); + sysPermission.setPermissionIdPath(codeBuffer.toString()); + sysPermission.setPermissionLevel(level); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionDataScopeService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionDataScopeService.java new file mode 100644 index 0000000..e083243 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionDataScopeService.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.system.entity.SysPositionDataScope; +import com.zerolinck.passiflora.system.mapper.SysPositionDataScopeMapper; +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-05-14 + */ +@Slf4j +@Service +public class SysPositionDataScopeService + extends ServiceImpl { + + private static final String LOCK_KEY = + "passiflora:lock:sysPositionDataScope:"; + + public Page page( + QueryCondition condition + ) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysPositionDataScope.class), + condition.sortWrapper(SysPositionDataScope.class) + ); + } + + public void add(SysPositionDataScope sysPositionDataScope) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysPositionDataScope); + baseMapper.insert(sysPositionDataScope); + return null; + } + ); + } + + public boolean update(SysPositionDataScope sysPositionDataScope) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysPositionDataScope); + int changeRowCount = baseMapper.updateById( + sysPositionDataScope + ); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection scopeIds) { + return baseMapper.deleteByIds(scopeIds, CurrentUtil.getCurrentUserId()); + } + + public SysPositionDataScope detail(String scopeId) { + SysPositionDataScope sysPositionDataScope = baseMapper.selectById( + scopeId + ); + if (sysPositionDataScope == null) { + throw new BizException("无对应职位数据权限数据,请刷新后重试"); + } + return sysPositionDataScope; + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionPermissionService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionPermissionService.java new file mode 100644 index 0000000..6d77163 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionPermissionService.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.system.entity.SysPositionPermission; +import com.zerolinck.passiflora.system.mapper.SysPositionPermissionMapper; +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-05-06 + */ +@Slf4j +@Service +public class SysPositionPermissionService + extends ServiceImpl { + + private static final String LOCK_KEY = + "passiflora:lock:sysPositionPermission:"; + + public Page page( + QueryCondition condition + ) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysPositionPermission.class), + condition.sortWrapper(SysPositionPermission.class) + ); + } + + public void add(SysPositionPermission sysPositionPermission) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysPositionPermission); + baseMapper.insert(sysPositionPermission); + return null; + } + ); + } + + public boolean update(SysPositionPermission sysPositionPermission) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysPositionPermission); + int changeRowCount = baseMapper.updateById( + sysPositionPermission + ); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection bindIds) { + return baseMapper.deleteByIds(bindIds, CurrentUtil.getCurrentUserId()); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByPositionIds(Collection positionIds) { + return baseMapper.deleteByPositionIds( + positionIds, + CurrentUtil.getCurrentUserId() + ); + } + + public SysPositionPermission detail(String bindId) { + SysPositionPermission sysPositionPermission = baseMapper.selectById( + bindId + ); + if (sysPositionPermission == null) { + throw new BizException("无对应系统职位菜单绑定数据,请刷新后重试"); + } + return sysPositionPermission; + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionService.java new file mode 100644 index 0000000..deacfa1 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysPositionService.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import com.zerolinck.passiflora.model.system.vo.SysPositionVo; +import com.zerolinck.passiflora.system.mapper.SysPositionMapper; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-05-14 + */ +@Slf4j +@Service +public class SysPositionService + extends ServiceImpl { + + private static final String LOCK_KEY = "passiflora:lock:sysPosition:"; + + public Page page(QueryCondition condition) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysPosition.class), + condition.sortWrapper(SysPosition.class) + ); + } + + public void add(SysPosition sysPosition) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock( + SysPosition::getPositionName, + sysPosition.getPositionName() + ), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysPosition); + generateIadPathAndLevel(sysPosition); + baseMapper.insert(sysPosition); + return null; + } + ); + } + + public boolean update(SysPosition sysPosition) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock( + SysPosition::getPositionName, + sysPosition.getPositionName() + ), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysPosition); + generateIadPathAndLevel(sysPosition); + int changeRowCount = baseMapper.updateById(sysPosition); + // 子机构数据变更 + List positionVoList = baseMapper.listByParentId( + sysPosition.getPositionId() + ); + positionVoList.forEach(positionVo -> { + generateIadPathAndLevel(positionVo); + baseMapper.updateById(positionVo); + }); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection positionIds) { + return baseMapper.deleteByIds( + positionIds, + CurrentUtil.getCurrentUserId() + ); + } + + public SysPosition detail(String positionId) { + SysPosition sysPosition = baseMapper.selectById(positionId); + if (sysPosition == null) { + throw new BizException("无对应数据,请刷新后重试"); + } + return sysPosition; + } + + public List positionTree() { + List sysPositionVos = baseMapper.listByParentId("0"); + sysPositionVos.forEach(this::recursionTree); + return sysPositionVos; + } + + @Transactional(rollbackFor = Exception.class) + public void disable(List positionIds) { + baseMapper.disable(positionIds, CurrentUtil.getCurrentUserId()); + } + + @Transactional(rollbackFor = Exception.class) + public void enable(List positionIds) { + List pathIds = new ArrayList<>(); + positionIds.forEach(positionId -> { + SysPosition sysPosition = baseMapper.selectById(positionId); + String[] positionIdList = sysPosition + .getPositionIdPath() + .split("/"); + Collections.addAll(pathIds, positionIdList); + }); + baseMapper.enable(pathIds, CurrentUtil.getCurrentUserId()); + } + + public void updateOrder(List sysPositionVos) { + if (CollectionUtil.isEmpty(sysPositionVos)) { + return; + } + for (SysPositionVo sysPositionTableVo : sysPositionVos) { + baseMapper.updateOrder(sysPositionTableVo); + updateOrder(sysPositionTableVo.getChildren()); + } + } + + private void recursionTree(SysPositionVo sysPositionVo) { + sysPositionVo.setChildren( + baseMapper.listByParentId(sysPositionVo.getPositionId()) + ); + sysPositionVo.getChildren().forEach(this::recursionTree); + } + + private void generateIadPathAndLevel(SysPosition sysPosition) { + StringBuilder codeBuffer = new StringBuilder(); + String positionParentId = sysPosition.getParentPositionId(); + int level = 0; + if (!"0".equals(positionParentId)) { + SysPosition parentOrg = detail(positionParentId); + codeBuffer.append(parentOrg.getPositionIdPath()).append("/"); + level = parentOrg.getPositionLevel() + 1; + } + codeBuffer.append(sysPosition.getPositionId()); + sysPosition.setPositionLevel(level); + sysPosition.setPositionIdPath(codeBuffer.toString()); + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserPositionService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserPositionService.java new file mode 100644 index 0000000..02e33a9 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserPositionService.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.system.entity.SysUserPosition; +import com.zerolinck.passiflora.system.mapper.SysUserPositionMapper; +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-05-14 + */ +@Slf4j +@Service +public class SysUserPositionService + extends ServiceImpl { + + private static final String LOCK_KEY = "passiflora:lock:sysUserPosition:"; + + public Page page( + QueryCondition condition + ) { + if (condition == null) { + condition = new QueryCondition<>(); + } + return baseMapper.page( + condition.page(), + condition.searchWrapper(SysUserPosition.class), + condition.sortWrapper(SysUserPosition.class) + ); + } + + public void add(SysUserPosition sysUserPosition) { + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysUserPosition); + baseMapper.insert(sysUserPosition); + return null; + } + ); + } + + public boolean update(SysUserPosition sysUserPosition) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper(), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysUserPosition); + int changeRowCount = baseMapper.updateById(sysUserPosition); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection bindIds) { + return baseMapper.deleteByIds(bindIds, CurrentUtil.getCurrentUserId()); + } + + public SysUserPosition detail(String bindId) { + SysUserPosition sysUserPosition = baseMapper.selectById(bindId); + if (sysUserPosition == null) { + throw new BizException("无对应用户职位绑定数据,请刷新后重试"); + } + return sysUserPosition; + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserService.java b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserService.java new file mode 100644 index 0000000..f61cf96 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/java/com/zerolinck/passiflora/system/service/SysUserService.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system.service; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerolinck.passiflora.common.config.PassifloraProperties; +import com.zerolinck.passiflora.common.exception.BizException; +import com.zerolinck.passiflora.common.util.CurrentUtil; +import com.zerolinck.passiflora.common.util.OnlyFieldCheck; +import com.zerolinck.passiflora.common.util.QueryCondition; +import com.zerolinck.passiflora.common.util.RedisUtils; +import com.zerolinck.passiflora.common.util.lock.LockUtil; +import com.zerolinck.passiflora.common.util.lock.LockWrapper; +import com.zerolinck.passiflora.model.common.constant.RedisPrefix; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import com.zerolinck.passiflora.model.system.mapperstruct.SysUserConvert; +import com.zerolinck.passiflora.model.system.vo.SysUserVo; +import com.zerolinck.passiflora.system.mapper.SysUserMapper; +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author linck + * @since 2024-02-07 + */ +@Service +public class SysUserService extends ServiceImpl { + + @Resource + private SysOrgService sysOrgService; + + @Resource + private PassifloraProperties passifloraProperties; + + private static final String LOCK_KEY = "passiflora:lock:sysUser:"; + + public Page page( + String orgId, + QueryCondition condition + ) { + if (condition == null) { + condition = new QueryCondition<>(); + } + Page page = baseMapper.page( + orgId, + condition.page(), + condition.searchWrapper(SysUser.class), + condition.sortWrapper(SysUser.class) + ); + + Map orgId2NameMap = sysOrgService.orgId2NameMap( + page.getRecords().stream().map(SysUser::getOrgId).toList() + ); + + List recordsVo = page + .getRecords() + .stream() + .map(sysUser -> { + SysUserVo sysUserVo = SysUserConvert.INSTANCE.entity2vo( + sysUser + ); + sysUserVo.setOrgName(orgId2NameMap.get(sysUser.getOrgId())); + return sysUserVo; + }) + .toList(); + + return new Page( + page.getCurrent(), + page.getSize(), + page.getTotal() + ) + .setRecords(recordsVo); + } + + public void add(SysUser sysUser) { + sysUser.setSalt(BCrypt.gensalt()); + sysUser.setUserPassword( + BCrypt.hashpw(sysUser.getUserPassword(), sysUser.getSalt()) + ); + + LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysUser::getUserName, sysUser.getUserName()), + () -> { + OnlyFieldCheck.checkInsert(baseMapper, sysUser); + baseMapper.insert(sysUser); + return null; + } + ); + } + + public boolean update(SysUser sysUser) { + return (boolean) LockUtil.lockAndTransactionalLogic( + LOCK_KEY, + new LockWrapper() + .lock(SysUser::getUserName, sysUser.getUserName()), + () -> { + OnlyFieldCheck.checkUpdate(baseMapper, sysUser); + int changeRowCount = baseMapper.updateById(sysUser); + return changeRowCount > 0; + } + ); + } + + @Transactional(rollbackFor = Exception.class) + public int deleteByIds(Collection userIds) { + return baseMapper.deleteByIds(userIds, CurrentUtil.getCurrentUserId()); + } + + public SysUser detail(String userId) { + SysUser sysUser = baseMapper.selectById(userId); + if (sysUser == null) { + throw new BizException("无对应用户数据,请刷新后重试"); + } + return sysUser; + } + + public String login(SysUser sysUser) { + SysUser dbSysUser = baseMapper.selectOne( + new LambdaQueryWrapper() + .eq(SysUser::getUserName, sysUser.getUserName()) + ); + if (dbSysUser == null) { + throw new BizException("账号或密码错误"); + } + if ( + !BCrypt.checkpw( + sysUser.getUserPassword(), + dbSysUser.getUserPassword() + ) + ) { + throw new BizException("账号或密码错误"); + } + String token = IdWorker.getIdStr(); + RedisUtils.set( + RedisPrefix.TOKEN_KEY + dbSysUser.getUserId() + ":" + token, + dbSysUser, + passifloraProperties.getSystem().getToken().getExpire() + ); + return token; + } + + public void logout() { + Set keys = RedisUtils.keys( + RedisPrefix.TOKEN_KEY + "*:" + CurrentUtil.getToken() + ); + if (CollectionUtil.isEmpty(keys)) { + return; + } + keys.forEach(RedisUtils::del); + } + + public Boolean checkToken() { + Set keys = RedisUtils.keys( + RedisPrefix.TOKEN_KEY + "*:" + CurrentUtil.getToken() + ); + if (CollectionUtil.isEmpty(keys)) { + return Boolean.FALSE; + } + keys.forEach(key -> + RedisUtils.expire( + key, + passifloraProperties.getSystem().getToken().getExpire() + ) + ); + return Boolean.TRUE; + } +} diff --git a/passiflora-server/passiflora-system-app/src/main/resources/application.yml b/passiflora-server/passiflora-system-app/src/main/resources/application.yml new file mode 100644 index 0000000..0bda809 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/application.yml @@ -0,0 +1,65 @@ +server: + port: 51001 + servlet: + context-path: /passiflora/system-api + + +spring: + application: + name: passiflora-system-app + profiles: + active: @env@ + cloud: + nacos: + discovery: + server-addr: @nacos.serverAddr@ + datasource: + username: @system_app.database.username@ + password: @system_app.database.password@ + url: @system_app.database.url@ + driver-class-name: org.postgresql.Driver + data: + redis: + database: @redis.database@ + host: @redis.host@ + port: @redis.port@ + password: @redis.password@ + +feign: + compression: + request: + enabled: true + mime-types: text/html,application/xml,application/json + min-request-size: 2048 + response: + enabled: true + client: + config: + default: + connect-timeout: 5000 + read-timeout: 600000 + +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + mapper-locations: classpath*:com/zerolinck/**/mapper/xml/*Mapper.xml + global-config: + db-config: + logic-delete-value: 1 + logic-not-delete-value: 0 + insert-strategy: not_null + update-strategy: not_null + +passiflora: + env: @env@ + system: + token: + expire: 604800 + config: + mybatis: true + exception: true + globalFilter: true + lock: true + cache: true + project-version: @projectVersion@ + diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202402041652_create_table_user.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202402041652_create_table_user.yml new file mode 100644 index 0000000..646c22f --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202402041652_create_table_user.yml @@ -0,0 +1,140 @@ +databaseChangeLog: + - changeSet: + id: 202402041652_create_table_user.yml + author: linck + changes: + - createTable: + tableName: sys_user + remarks: 系统用户 + columns: + - column: + name: user_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: user_name + type: varchar(50) + constraints: + nullable: false + remarks: 用户名 + - column: + name: real_name + type: varchar(50) + constraints: + nullable: false + remarks: 真实姓名 + - column: + name: id_card_no + type: varchar(50) + constraints: + nullable: true + remarks: 身份证号 + - column: + name: date_of_birth + type: date + constraints: + nullable: true + remarks: 出生日期 + - column: + name: gender + type: int2 + constraints: + nullable: true + remarks: 性别 + - column: + name: phone_num + type: varchar(30) + constraints: + nullable: true + remarks: 手机号 + - column: + name: email + type: varchar(50) + constraints: + nullable: true + remarks: 电子邮箱 + - column: + name: avatar_file + type: varchar(20) + constraints: + nullable: true + remarks: 用户头像 + - column: + name: remark + type: varchar(200) + constraints: + nullable: true + remarks: 备注 + - column: + name: user_password + type: varchar(50) + constraints: + nullable: false + remarks: 用户密码 + - column: + name: salt + type: varchar(50) + constraints: + nullable: false + remarks: 密码盐 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 + + - changeSet: + id: 202402041652_add_column_user + author: linck + changes: + - insert: + tableName: sys_user + columns: + - column: + name: user_id + value: '1' + - column: + name: user_name + value: 'admin' + - column: + name: real_name + value: '系统管理员' + - column: + name: user_password + value: 'de4a48f993722408b56f3b6657146f30' + - column: + name: salt + value: '77a8e58a45c992afbdd3b9c6a5a13536' \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict.yml new file mode 100644 index 0000000..0164dd0 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict.yml @@ -0,0 +1,81 @@ +databaseChangeLog: + - changeSet: + id: 202404011823_create_table_dict + author: liquibase + changes: + - createTable: + tableName: sys_dict + remarks: 字典 + columns: + - column: + name: dict_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键ID + - column: + name: dict_name + type: varchar(20) + constraints: + nullable: false + remarks: 字典名称 + - column: + name: description + type: varchar(200) + remarks: 描述 + - column: + name: is_system + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 是否系统内置 0-否 1-是,系统内置不允许删除 + - column: + name: item_value_is_only + type: int2 + constraints: + nullable: false + defaultValue: 1 + remarks: 字典项值是否唯一 0-否 1-是 + - column: + name: dict_tag + type: varchar(100) + constraints: + nullable: false + remarks: 字典标识 + - column: + name: create_by + type: varchar(20) + remarks: 创建人 + - column: + name: update_by + type: varchar(20) + remarks: 更新人 + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标志 0-未删除 1-已删除 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict_item.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict_item.yml new file mode 100644 index 0000000..2d84a95 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404011823_create_table_dict_item.yml @@ -0,0 +1,80 @@ +databaseChangeLog: + - changeSet: + id: 202404011823_create_table_dict_item + author: liquibase + changes: + - createTable: + tableName: sys_dict_item + remarks: 字典项 + columns: + - column: + name: dict_item_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键ID + - column: + name: dict_id + type: varchar(20) + constraints: + nullable: false + remarks: 字典类别id + - column: + name: item_name + type: varchar(100) + constraints: + nullable: false + remarks: 字典项名称 + - column: + name: item_value + type: varchar(100) + constraints: + nullable: false + remarks: 字典项值 + - column: + name: description + type: varchar(200) + remarks: 描述 + - column: + name: is_system + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 是否系统内置 0-否 1-是,系统内置不允许删除 + - column: + name: create_by + type: varchar(20) + remarks: 创建人 + - column: + name: update_by + type: varchar(20) + remarks: 更新人 + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标志 0-未删除 1-已删除 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031442_dict_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031442_dict_rename_column.yml new file mode 100644 index 0000000..bd79e2a --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031442_dict_rename_column.yml @@ -0,0 +1,19 @@ +databaseChangeLog: + - changeSet: + id: 202404031442_dict_rename_column_1.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_dict + oldColumnName: description + newColumnName: remark + - changeSet: + id: 202404031442_dict_rename_column_2.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_dict_item + oldColumnName: description + newColumnName: remark \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031706_dict_item_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031706_dict_item_rename_column.yml new file mode 100644 index 0000000..5c5d366 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031706_dict_item_rename_column.yml @@ -0,0 +1,19 @@ +databaseChangeLog: + - changeSet: + id: 202404031442_dict_rename_column_1.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_dict_item + oldColumnName: item_name + newColumnName: label + - changeSet: + id: 202404031442_dict_rename_column_2.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_dict_item + oldColumnName: item_value + newColumnName: value \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031709_dict_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031709_dict_rename_column.yml new file mode 100644 index 0000000..fe91ad4 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404031709_dict_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202404031442_dict_rename_column_1.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_dict + oldColumnName: item_value_is_only + newColumnName: value_is_only \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404081703_dict_data.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404081703_dict_data.yml new file mode 100644 index 0000000..77ab503 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404081703_dict_data.yml @@ -0,0 +1,130 @@ +databaseChangeLog: + - changeSet: + id: 202404081703_dict_data.yml_1 + author: linck + changes: + - insert: + tableName: sys_dict + columns: + - column: + name: dict_id + value: '1777220357552373762' + - column: + name: dict_name + value: '性别' + - column: + name: remark + value: null + - column: + name: is_system + value: 1 + - column: + name: value_is_only + value: 0 + - column: + name: dict_tag + value: 'gender' + - column: + name: create_by + value: null + - column: + name: update_by + value: null + - column: + name: create_time + value: '2024-04-08 14:22:07.108246' + - column: + name: update_time + value: '2024-04-08 14:22:07.108246' + - column: + name: del_flag + value: 0 + - column: + name: version + value: 0 + - changeSet: + id: 202404081703_dict_data.yml_2 + author: linck + changes: + - insert: + tableName: sys_dict_item + columns: + - column: + name: dict_item_id + value: '1777223927664246785' + - column: + name: dict_id + value: '1777220357552373762' + - column: + name: label + value: '女' + - column: + name: value + value: '0' + - column: + name: remark + value: null + - column: + name: is_system + value: 1 + - column: + name: create_by + value: null + - column: + name: update_by + value: null + - column: + name: create_time + value: '2024-04-08 14:36:18.239823' + - column: + name: update_time + value: '2024-04-08 14:36:18.239823' + - column: + name: del_flag + value: 0 + - column: + name: version + value: 0 + - changeSet: + id: 202404081703_dict_data.yml_3 + author: linck + changes: + - insert: + tableName: sys_dict_item + columns: + - column: + name: dict_item_id + value: '1777223959134109697' + - column: + name: dict_id + value: '1777220357552373762' + - column: + name: label + value: '男' + - column: + name: value + value: '1' + - column: + name: remark + value: null + - column: + name: is_system + value: 1 + - column: + name: create_by + value: null + - column: + name: update_by + value: null + - column: + name: create_time + value: '2024-04-08 14:36:25.741143' + - column: + name: update_time + value: '2024-04-08 14:36:25.741143' + - column: + name: del_flag + value: 0 + - column: + name: version + value: 0 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092001_create_table_org.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092001_create_table_org.yml new file mode 100644 index 0000000..1a336a7 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092001_create_table_org.yml @@ -0,0 +1,88 @@ +databaseChangeLog: + - changeSet: + id: 202404092001_create_table_org + author: linck + changes: + - createTable: + tableName: sys_org + columns: + - column: + name: org_id + type: varchar(20) + constraints: + primaryKey: true + nullable: false + remarks: 机构ID + - column: + name: org_name + type: varchar(100) + constraints: + nullable: false + remarks: 机构名称 + - column: + name: org_code + type: varchar(100) + constraints: + nullable: false + remarks: 机构编码 + - column: + name: org_type + type: int2 + constraints: + nullable: false + remarks: 机构类型 + - column: + name: org_level + type: int2 + constraints: + nullable: false + remarks: 机构级别 + - column: + name: parent_org_id + type: varchar(20) + constraints: + nullable: false + defaultValue: '0' + remarks: 父机构ID + - column: + name: org_id_path + type: varchar(200) + constraints: + nullable: false + remarks: 机构ID路径 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092002_set_comment_table_org.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092002_set_comment_table_org.yml new file mode 100644 index 0000000..aea9cb5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404092002_set_comment_table_org.yml @@ -0,0 +1,8 @@ +databaseChangeLog: + - changeSet: + id: 202404092002_add_comment_table_org + author: linck + changes: + - setTableRemarks: + tableName: sys_org + remarks: "机构" diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404112001_add_column_default_value_org.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404112001_add_column_default_value_org.yml new file mode 100644 index 0000000..cb3e2f7 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404112001_add_column_default_value_org.yml @@ -0,0 +1,9 @@ +databaseChangeLog: + - changeSet: + id: 202404112001_add_column_default_value_org + author: linck + changes: + - addDefaultValue: + tableName: sys_org + columnName: del_flag + defaultValue: 0 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404282001_user_change_column_type.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404282001_user_change_column_type.yml new file mode 100644 index 0000000..05b033b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404282001_user_change_column_type.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202404282001_user_change_column_type.yml + author: linck + changes: + - modifyDataType: + schemaName: public + tableName: sys_user + columnName: user_password + newDataType: varchar(100) \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404292001_user_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404292001_user_add_column.yml new file mode 100644 index 0000000..fd4add2 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202404292001_user_add_column.yml @@ -0,0 +1,18 @@ +databaseChangeLog: + - changeSet: + id: 2024040292001_user_add_column_1 + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_user + columns: + - column: + name: org_id + type: varchar(20) + defaultValue: '0' + + - dropDefaultValue: + schemaName: public + tableName: sys_user + columnName: org_id \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062001_create_table_menu.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062001_create_table_menu.yml new file mode 100644 index 0000000..743fbe0 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062001_create_table_menu.yml @@ -0,0 +1,160 @@ +databaseChangeLog: + - changeSet: + id: 202405062001_create_table_menu + author: linck + changes: + - createTable: + tableName: sys_menu + remarks: 系统菜单 + columns: + - column: + name: menu_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: title + type: varchar(50) + constraints: + nullable: false + remarks: 标题 + - column: + name: name + type: varchar(50) + constraints: + nullable: false + remarks: 名称 + - column: + name: local + type: varchar(50) + constraints: + nullable: true + remarks: 国际化 + - column: + name: icon + type: varchar(50) + constraints: + nullable: true + remarks: 图标 + - column: + name: parent_id + type: varchar(20) + constraints: + nullable: false + defaultValue: 0 + remarks: 上级ID + - column: + name: menu_id_path + type: varchar(200) + constraints: + nullable: false + remarks: ID路径 + - column: + name: sort + type: int4 + constraints: + nullable: false + defaultValue: 0 + remarks: 排序 + - column: + name: remark + type: varchar(200) + constraints: + nullable: true + remarks: 备注 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 + + - createTable: + tableName: sys_position_menu + remarks: 系统职位菜单绑定 + columns: + - column: + name: bind_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: position_id + type: varchar(20) + constraints: + nullable: false + remarks: 职位ID + - column: + name: menu_id + type: varchar(20) + constraints: + nullable: false + remarks: 菜单ID + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062002_create_table_permission.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062002_create_table_permission.yml new file mode 100644 index 0000000..6ceb29d --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062002_create_table_permission.yml @@ -0,0 +1,141 @@ +databaseChangeLog: + - changeSet: + id: 202405062002_create_table_permission + author: linck + changes: + - createTable: + tableName: sys_permission + remarks: 系统权限 + columns: + - column: + name: permission_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: title + type: varchar(50) + constraints: + nullable: false + remarks: 标题 + - column: + name: name + type: varchar(50) + constraints: + nullable: false + remarks: 名称 + - column: + name: parent_menu_id + type: varchar(20) + constraints: + nullable: false + remarks: 上级菜单ID + - column: + name: sort + type: int4 + constraints: + nullable: false + defaultValue: 0 + remarks: 排序 + - column: + name: remark + type: varchar(200) + constraints: + nullable: true + remarks: 备注 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 + + - createTable: + tableName: sys_position_permission + remarks: 系统职位菜单绑定 + columns: + - column: + name: bind_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: position_id + type: varchar(20) + constraints: + nullable: false + remarks: 职位ID + - column: + name: permission_id + type: varchar(20) + constraints: + nullable: false + remarks: 权限ID + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062003_menu_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062003_menu_add_column.yml new file mode 100644 index 0000000..11a2e76 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405062003_menu_add_column.yml @@ -0,0 +1,16 @@ +databaseChangeLog: + - changeSet: + id: 202405062003_menu_add_column + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_menu + columns: + - column: + name: level + type: int2 + constraints: + nullable: false + defaultValue: 1 + remarks: 级别 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_add_column.yml new file mode 100644 index 0000000..851715f --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_add_column.yml @@ -0,0 +1,22 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_menu_add_column + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_menu + columns: + - column: + name: status + type: int2 + defaultValue: 1 + + - addColumn: + schemaName: public + tableName: sys_permission + columns: + - column: + name: status + type: int2 + defaultValue: 1 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_modify_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_modify_column.yml new file mode 100644 index 0000000..e96f217 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_modify_column.yml @@ -0,0 +1,27 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_menu_add_column + author: linck + changes: + - addNotNullConstraint: + schemaName: public + tableName: sys_menu + columnName: status + + + - addNotNullConstraint: + schemaName: public + tableName: sys_permission + columnName: status + + - setColumnRemarks: + schemaName: public + tableName: sys_menu + columnName: status + remarks: 状态 + + - setColumnRemarks: + schemaName: public + tableName: sys_permission + columnName: status + remarks: 状态 \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_rename_column.yml new file mode 100644 index 0000000..fa19886 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_menu_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_menu_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_menu + oldColumnName: sort + newColumnName: order \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_permission_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_permission_rename_column.yml new file mode 100644 index 0000000..640b99b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072001_permission_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_permission_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: sort + newColumnName: order \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_add_column.yml new file mode 100644 index 0000000..76e8546 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_add_column.yml @@ -0,0 +1,16 @@ +databaseChangeLog: + - changeSet: + id: 202405072002_menu_add_column + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_menu + columns: + - column: + name: menu_type + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 类型 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_rename_column.yml new file mode 100644 index 0000000..d4869ec --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405072002_menu_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405072001_menu_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_menu + oldColumnName: local + newColumnName: locale \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_drop_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_drop_column.yml new file mode 100644 index 0000000..ea0e281 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_drop_column.yml @@ -0,0 +1,11 @@ +databaseChangeLog: + - changeSet: + id: 202405092003_menu_drop_column + author: linck + changes: + - dropColumn: + schemaName: public + tableName: sys_menu + columns: + - column: + name: locale diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_drop.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_drop.yml new file mode 100644 index 0000000..5681f75 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_drop.yml @@ -0,0 +1,8 @@ +databaseChangeLog: + - changeSet: + id: 202405092003_menu_table_rename.yml + author: linck + changes: + - dropTable: + schemaName: public + tableName: sys_position_menu diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_rename.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_rename.yml new file mode 100644 index 0000000..5ec59dd --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405092003_menu_table_rename.yml @@ -0,0 +1,13 @@ +databaseChangeLog: + - changeSet: + id: 202405092003_menu_table_rename + author: linck + changes: + - dropTable: + schemaName: public + tableName: sys_permission + + - renameTable: + schemaName: public + oldTableName: sys_menu + newTableName: sys_permission diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102002_permission_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102002_permission_rename_column.yml new file mode 100644 index 0000000..65b3ec0 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102002_permission_rename_column.yml @@ -0,0 +1,16 @@ +databaseChangeLog: + - changeSet: + id: 202405102002_permission_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: menu_id + newColumnName: permission_id + + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: menu_type + newColumnName: permission_type \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102003_permission_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102003_permission_rename_column.yml new file mode 100644 index 0000000..73f8f51 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405102003_permission_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405102003_permission_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: menu_id_path + newColumnName: permission_id_path diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142001_create_table_position.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142001_create_table_position.yml new file mode 100644 index 0000000..10f1121 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142001_create_table_position.yml @@ -0,0 +1,83 @@ +databaseChangeLog: + - changeSet: + id: 202405142001_create_table_position.yml + author: linck + changes: + - createTable: + tableName: sys_position + columns: + - column: + name: position_id + type: varchar(20) + constraints: + primaryKey: true + nullable: false + remarks: 职位ID + - column: + name: position_name + type: varchar(100) + constraints: + nullable: false + remarks: 职位名称 + - column: + name: position_level + type: int2 + constraints: + nullable: false + remarks: 职位级别 + - column: + name: parent_position_id + type: varchar(20) + constraints: + nullable: false + defaultValue: 0 + remarks: 父职位ID + - column: + name: data_scope_type + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 数据范围类型 + - column: + name: position_id_path + type: varchar(200) + constraints: + nullable: false + remarks: 职位ID路径 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_position_data_scope.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_position_data_scope.yml new file mode 100644 index 0000000..f327a33 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_position_data_scope.yml @@ -0,0 +1,70 @@ +databaseChangeLog: + - changeSet: + id: 202405142002_create_table_position_data_scope + author: linck + changes: + - createTable: + tableName: sys_position_data_scope + remarks: 职位数据权限 + columns: + - column: + name: scope_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: position_id + type: varchar(20) + constraints: + nullable: false + remarks: 职位ID + - column: + name: org_id + type: varchar(20) + constraints: + nullable: false + remarks: 机构ID + - column: + name: data_scope + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 数据范围 + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_user_position.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_user_position.yml new file mode 100644 index 0000000..5221295 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405142002_create_table_user_position.yml @@ -0,0 +1,63 @@ +databaseChangeLog: + - changeSet: + id: 202405142002_create_table_user_position + author: linck + changes: + - createTable: + tableName: sys_user_position + remarks: 用户职位绑定 + columns: + - column: + name: bind_id + type: varchar(20) + constraints: + primaryKey: true + remarks: 主键 + - column: + name: user_id + type: varchar(20) + constraints: + nullable: false + remarks: 用户ID + - column: + name: position_id + type: varchar(20) + constraints: + nullable: false + remarks: 职位ID + - column: + name: create_by + type: varchar(20) + remarks: 创建用户id + - column: + name: update_by + type: varchar(20) + remarks: 更新用户id + - column: + name: create_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 创建时间 + - column: + name: update_time + type: timestamp(6) + constraints: + nullable: false + defaultValueComputed: CURRENT_TIMESTAMP + remarks: 更新时间 + - column: + name: del_flag + type: int2 + constraints: + nullable: false + defaultValue: 0 + remarks: 删除标识 + - column: + name: version + type: int8 + constraints: + nullable: false + defaultValue: 0 + remarks: 乐观锁版本 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_column.yml new file mode 100644 index 0000000..92990e1 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_column.yml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + id: 202405292001_org_add_column.yml + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_org + columns: + - column: + name: org_order + type: int4 + defaultValue: 1 + remarks: 机构排序 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_constraint.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_constraint.yml new file mode 100644 index 0000000..b143cf5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_org_add_constraint.yml @@ -0,0 +1,9 @@ +databaseChangeLog: + - changeSet: + id: 202405292002_position_add_column.yml + author: linck + changes: + - addNotNullConstraint: + schemaName: public + tableName: sys_org + columnName: org_order diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_position_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_position_add_column.yml new file mode 100644 index 0000000..b6d3fd5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292001_position_add_column.yml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + id: 202405292001_position_add_column + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_position + columns: + - column: + name: position_status + type: int2 + defaultValue: 1 + remarks: 职位状态 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292002_position_add_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292002_position_add_column.yml new file mode 100644 index 0000000..3038901 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292002_position_add_column.yml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + id: 202405292002_position_add_column.yml + author: linck + changes: + - addColumn: + schemaName: public + tableName: sys_position + columns: + - column: + name: order + type: int4 + defaultValue: 1 + remarks: 排序 diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292003_position_add_constraint.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292003_position_add_constraint.yml new file mode 100644 index 0000000..1b1e696 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405292003_position_add_constraint.yml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + id: 202405292003_position_add_constraint.yml + author: linck + changes: + - addNotNullConstraint: + schemaName: public + tableName: sys_position + columnName: position_status + + - addNotNullConstraint: + schemaName: public + tableName: sys_position + columnName: order diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302001_user_add_constraint.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302001_user_add_constraint.yml new file mode 100644 index 0000000..01506da --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302001_user_add_constraint.yml @@ -0,0 +1,9 @@ +databaseChangeLog: + - changeSet: + id: 202405302001_user_add_constraint.yml + author: linck + changes: + - addNotNullConstraint: + schemaName: public + tableName: sys_user + columnName: org_id diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302002_org_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302002_org_rename_column.yml new file mode 100644 index 0000000..1c1a0d9 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302002_org_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405302002_org_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_org + oldColumnName: org_order + newColumnName: order \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302003_permission_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302003_permission_rename_column.yml new file mode 100644 index 0000000..b7d5bac --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302003_permission_rename_column.yml @@ -0,0 +1,10 @@ +databaseChangeLog: + - changeSet: + id: 202405302003_permission_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: parent_id + newColumnName: permission_parent_id \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302004_permission_rename_column.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302004_permission_rename_column.yml new file mode 100644 index 0000000..73a9456 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/changelogs/202405302004_permission_rename_column.yml @@ -0,0 +1,34 @@ +databaseChangeLog: + - changeSet: + id: 202405302004_permission_rename_column.yml + author: linck + changes: + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: title + newColumnName: permission_title + + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: name + newColumnName: permission_name + + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: icon + newColumnName: permission_icon + + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: level + newColumnName: permission_level + + - renameColumn: + schemaName: public + tableName: sys_permission + oldColumnName: status + newColumnName: permission_status \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/db/main.yml b/passiflora-server/passiflora-system-app/src/main/resources/db/main.yml new file mode 100644 index 0000000..be0678f --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/db/main.yml @@ -0,0 +1,3 @@ +databaseChangeLog: + - includeAll: + path: src/main/resources/db/changelogs \ No newline at end of file diff --git a/passiflora-server/passiflora-system-app/src/main/resources/log4j2-spring.xml b/passiflora-server/passiflora-system-app/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..58bd635 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/main/resources/log4j2-spring.xml @@ -0,0 +1,64 @@ + + + + + + + + + + ${spring:spring.application.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/EnumControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/EnumControllerTest.java new file mode 100644 index 0000000..825e019 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/EnumControllerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-04-25 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class EnumControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + @Order(1) + public void testList() throws Exception { + mockMvc + .perform(get("/enum/OrgLevelEnum")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysDictControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysDictControllerTest.java new file mode 100644 index 0000000..15458a7 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysDictControllerTest.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.system.entity.SysDict; +import com.zerolinck.passiflora.model.system.entity.SysDictItem; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-04-01 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SysDictControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testSysDictId; + private static SysDict testSysDict; + + private static String testSysDictItemId; + + private static SysDictItem testSysDictItem; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/sysDict/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testItemPage() throws Exception { + mockMvc + .perform( + get("/sysDictItem/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(3) + public void testAdd() throws Exception { + SysDict sysDict = new SysDict(); + sysDict.setDictName("test"); + sysDict.setDictTag("test"); + mockMvc + .perform( + post("/sysDict/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysDict)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testSysDictId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(4) + public void testAddItem() throws Exception { + SysDictItem sysDictItem = new SysDictItem(); + sysDictItem.setDictId(testSysDictId); + sysDictItem.setLabel("test"); + sysDictItem.setValue("test"); + mockMvc + .perform( + post("/sysDictItem/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysDictItem)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testSysDictItemId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(5) + public void testDetailItem() throws Exception { + mockMvc + .perform( + get("/sysDictItem/detail") + .param("dictItemId", testSysDictItemId) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testSysDictItem = + objectMapper.convertValue( + jsonNode.get("data"), + SysDictItem.class + ); + }); + } + + @Test + @Order(6) + public void testUpdateItem() throws Exception { + mockMvc + .perform( + post("/sysDictItem/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testSysDictItem)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(7) + public void testDeleteItem() throws Exception { + mockMvc + .perform( + post("/sysDictItem/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysDictItemId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(8) + public void testDetail() throws Exception { + mockMvc + .perform(get("/sysDict/detail").param("dictId", testSysDictId)) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testSysDict = + objectMapper.convertValue(jsonNode.get("data"), SysDict.class); + }); + } + + @Test + @Order(9) + public void testUpdate() throws Exception { + mockMvc + .perform( + post("/sysDict/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testSysDict)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(10) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/sysDict/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysDictId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(11) + public void testListByDictId() throws Exception { + mockMvc + .perform( + get("/sysDictItem/listByDictId") + .param("dictId", "1778631179896446977") + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(12) + public void testListByDictName() throws Exception { + mockMvc + .perform( + get("/sysDictItem/listByDictName").param("dictName", "机构类型") + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(13) + public void testListByDictTag() throws Exception { + mockMvc + .perform( + get("/sysDictItem/listByDictTag").param("dictTag", "orgType") + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysOrgControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysOrgControllerTest.java new file mode 100644 index 0000000..08ea44b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysOrgControllerTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.system.entity.SysOrg; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-04-09 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SysOrgControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testSysOrgId; + private static SysOrg testSysOrg; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/sysOrg/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testAdd() throws Exception { + SysOrg sysOrg = new SysOrg(); + sysOrg.setOrgName("test"); + sysOrg.setOrgCode("test"); + sysOrg.setOrgLevel(1); + sysOrg.setOrgType(1); + mockMvc + .perform( + post("/sysOrg/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysOrg)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testSysOrgId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(3) + public void testDetail() throws Exception { + mockMvc + .perform(get("/sysOrg/detail").param("orgId", testSysOrgId)) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testSysOrg = + objectMapper.convertValue(jsonNode.get("data"), SysOrg.class); + }); + } + + @Test + @Order(4) + public void testUpdate() throws Exception { + mockMvc + .perform( + post("/sysOrg/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testSysOrg)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(5) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/sysOrg/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysOrgId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(6) + public void testListByParentId() throws Exception { + mockMvc + .perform(get("/sysOrg/listByParentId")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(7) + public void testOrgTree() throws Exception { + mockMvc + .perform(get("/sysOrg/orgTree")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPermissionControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPermissionControllerTest.java new file mode 100644 index 0000000..195022b --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPermissionControllerTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.system.entity.SysPermission; +import com.zerolinck.passiflora.model.system.enums.PermissionTypeEnum; +import com.zerolinck.passiflora.model.system.vo.SysPermissionTableVo; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-05-06 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SysPermissionControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testSysPermissionId; + + private static SysPermission testSysPermission; + + private static List permissionTree; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/sysPermission/page") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testAdd() throws Exception { + SysPermission sysPermission = new SysPermission(); + sysPermission.setPermissionId("test"); + sysPermission.setPermissionTitle("test"); + sysPermission.setPermissionName("test"); + sysPermission.setPermissionIdPath("test"); + sysPermission.setPermissionType(PermissionTypeEnum.MENU_SET); + mockMvc + .perform( + post("/sysPermission/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysPermission)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testSysPermissionId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(3) + public void testDetail() throws Exception { + mockMvc + .perform( + get("/sysPermission/detail") + .param("permissionId", testSysPermissionId) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testSysPermission = + objectMapper.convertValue( + jsonNode.get("data"), + SysPermission.class + ); + }); + } + + @Test + @Order(4) + public void testUpdate() throws Exception { + mockMvc + .perform( + post("/sysPermission/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testSysPermission)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(5) + public void testMenuTree() throws Exception { + mockMvc + .perform(get("/sysPermission/menuTree")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(6) + public void testPermissionTree() throws Exception { + mockMvc + .perform(get("/sysPermission/permissionTableTree")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + permissionTree = + objectMapper.convertValue( + jsonNode.get("data"), + new TypeReference<>() {} + ); + }); + } + + @Test + @Order(7) + public void testDisable() throws Exception { + mockMvc + .perform( + post("/sysPermission/disable") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPermissionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(8) + public void testEnable() throws Exception { + mockMvc + .perform( + post("/sysPermission/enable") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPermissionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(9) + public void testUpdateOrder() throws Exception { + mockMvc + .perform( + post("/sysPermission/updateOrder") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(permissionTree)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(10) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/sysPermission/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPermissionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPositionControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPositionControllerTest.java new file mode 100644 index 0000000..90f7b1e --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysPositionControllerTest.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.common.enums.StatusEnum; +import com.zerolinck.passiflora.model.system.entity.SysPosition; +import com.zerolinck.passiflora.model.system.enums.PositionDataScopeTypeEnum; +import com.zerolinck.passiflora.model.system.vo.SysPositionVo; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-05-14 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SysPositionControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testSysPositionId; + + private static SysPosition testSysPosition; + + private static List positionTree; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/sysPosition/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testAdd() throws Exception { + SysPosition sysPosition = new SysPosition(); + sysPosition.setPositionId("test"); + sysPosition.setPositionName("test"); + sysPosition.setPositionIdPath("test"); + sysPosition.setDataScopeType(PositionDataScopeTypeEnum.ALL); + sysPosition.setPositionLevel(1); + sysPosition.setPositionStatus(StatusEnum.ENABLE); + mockMvc + .perform( + post("/sysPosition/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysPosition)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testSysPositionId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(3) + public void testDetail() throws Exception { + mockMvc + .perform( + get("/sysPosition/detail") + .param("positionId", testSysPositionId) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testSysPosition = + objectMapper.convertValue( + jsonNode.get("data"), + SysPosition.class + ); + }); + } + + @Test + @Order(4) + public void testUpdate() throws Exception { + mockMvc + .perform( + post("/sysPosition/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testSysPosition)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(5) + public void testPermissionTree() throws Exception { + mockMvc + .perform(get("/sysPosition/positionTree")) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + positionTree = + objectMapper.convertValue( + jsonNode.get("data"), + new TypeReference<>() {} + ); + }); + } + + @Test + @Order(6) + public void testDisable() throws Exception { + mockMvc + .perform( + post("/sysPosition/disable") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPositionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(7) + public void testEnable() throws Exception { + mockMvc + .perform( + post("/sysPosition/enable") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPositionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(8) + public void testUpdateOrder() throws Exception { + mockMvc + .perform( + post("/sysPosition/updateOrder") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(positionTree)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(9) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/sysPosition/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testSysPositionId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysUserControllerTest.java b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysUserControllerTest.java new file mode 100644 index 0000000..4ff3de5 --- /dev/null +++ b/passiflora-server/passiflora-system-app/src/test/java/com/zerolinck/passiflora/system/SysUserControllerTest.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2024 Linck. + * + * 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 . + */ +package com.zerolinck.passiflora.system; + +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zerolinck.passiflora.common.api.ResultCodeEnum; +import com.zerolinck.passiflora.model.common.constant.Constants; +import com.zerolinck.passiflora.model.system.entity.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author linck + * @since 2024-03-20 + */ +@Slf4j +@SpringBootTest +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SysUserControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + private static String testUserId; + private static SysUser testUser; + private static String testToken; + + @Test + @Order(1) + public void testPage() throws Exception { + mockMvc + .perform( + get("/sysUser/page").contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(2) + public void testAdd() throws Exception { + SysUser sysUser = new SysUser(); + sysUser.setUserName("test"); + sysUser.setRealName("test"); + sysUser.setUserPassword("test"); + sysUser.setOrgId("test"); + mockMvc + .perform( + post("/sysUser/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(sysUser)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testUserId = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + System.out.println(); + } + + @Test + @Order(3) + public void testDetail() throws Exception { + mockMvc + .perform(get("/sysUser/detail").param("userId", testUserId)) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> { + String responseBody = result.getResponse().getContentAsString(); + JsonNode jsonNode = objectMapper.readTree(responseBody); + testUser = + objectMapper.convertValue(jsonNode.get("data"), SysUser.class); + }); + System.out.println(); + } + + @Test + @Order(4) + public void testUpdate() throws Exception { + mockMvc + .perform( + post("/sysUser/update") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testUser)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(5) + public void testLogin() throws Exception { + testUser.setUserName("test"); + testUser.setUserPassword("test"); + mockMvc + .perform( + post("/sysUser/login") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(testUser)) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ) + .andDo(result -> + testToken = + objectMapper + .readTree(result.getResponse().getContentAsString()) + .get("data") + .asText() + ); + } + + @Test + @Order(6) + public void testCheckToken() throws Exception { + mockMvc + .perform( + get("/sysUser/checkToken") + .header(Constants.Authorization, testToken) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(7) + public void testCurrentUserInfo() throws Exception { + mockMvc + .perform( + get("/sysUser/currentUserInfo") + .header(Constants.Authorization, testToken) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(8) + public void testLogout() throws Exception { + mockMvc + .perform( + get("/sysUser/logout") + .header(Constants.Authorization, testToken) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } + + @Test + @Order(9) + public void testDelete() throws Exception { + mockMvc + .perform( + post("/sysUser/delete") + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString( + new String[] { testUserId } + ) + ) + ) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$.code", equalTo(ResultCodeEnum.SUCCESS.getCode())) + ); + } +} diff --git a/passiflora-server/settings.gradle b/passiflora-server/settings.gradle new file mode 100644 index 0000000..3d96584 --- /dev/null +++ b/passiflora-server/settings.gradle @@ -0,0 +1,11 @@ +rootProject.name = 'passiflora' + +include 'modules:passiflora-common' +include 'modules:passiflora-bom' +include 'modules:passiflora-model' +include 'modules:passiflora-feign' +include 'passiflora-gateway-app' +include 'passiflora-system-app' +include 'modules:passiflora-codegen' +include 'passiflora-storage-app' + diff --git a/passiflora-ui/.env.development b/passiflora-ui/.env.development new file mode 100644 index 0000000..5ef958d --- /dev/null +++ b/passiflora-ui/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL= '/passiflora' \ No newline at end of file diff --git a/passiflora-ui/.env.production b/passiflora-ui/.env.production new file mode 100644 index 0000000..32ff92f --- /dev/null +++ b/passiflora-ui/.env.production @@ -0,0 +1 @@ +VITE_API_BASE_URL= '/passiflora' diff --git a/passiflora-ui/.eslintignore b/passiflora-ui/.eslintignore new file mode 100644 index 0000000..fc04190 --- /dev/null +++ b/passiflora-ui/.eslintignore @@ -0,0 +1,3 @@ +/*.json +/*.js +dist \ No newline at end of file diff --git a/passiflora-ui/.eslintrc.js b/passiflora-ui/.eslintrc.js new file mode 100644 index 0000000..ef2005e --- /dev/null +++ b/passiflora-ui/.eslintrc.js @@ -0,0 +1,70 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + root: true, + parser: 'vue-eslint-parser', + parserOptions: { + // Parser that checks the content of the + + diff --git a/passiflora-ui/nginx.conf b/passiflora-ui/nginx.conf new file mode 100644 index 0000000..6845c41 --- /dev/null +++ b/passiflora-ui/nginx.conf @@ -0,0 +1,51 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile on; + tcp_nopush on; + keepalive_timeout 600s; + gzip on; + + server { + listen 80; + + send_timeout 600s; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + + location / { + root /usr/share/nginx/dist; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location /passiflora/ { + client_max_body_size 2048M; + proxy_pass http://127.0.0.1:51000; + # 不缓存,解决 SSE 响应被缓存问题 + proxy_buffering off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/passiflora-ui/package.json b/passiflora-ui/package.json new file mode 100644 index 0000000..42d5a5a --- /dev/null +++ b/passiflora-ui/package.json @@ -0,0 +1,103 @@ +{ + "name": "passiflora-ui", + "description": "Passiflora", + "version": "1.0.0", + "private": true, + "author": "ArcoDesign Team", + "license": "MIT", + "scripts": { + "dev": "vite --config ./config/vite.config.dev.ts", + "build": "vite build --config ./config/vite.config.prod.ts", + "report": "cross-env REPORT=true npm run build", + "preview": "npm run build && vite preview --host", + "type:check": "vue-tsc --noEmit --skipLibCheck", + "lint-staged": "npx lint-staged '**/*.scss'", + "prettier": "prettier --write 'src/**/*.{js,jsx,ts,tsx,vue}'" + }, + "lint-staged": { + "*.{js,ts,jsx,tsx}": [ + "prettier --write", + "eslint --fix" + ], + "*.vue": [ + "stylelint --fix", + "prettier --write", + "eslint --fix" + ], + "*.{less,css}": [ + "stylelint --fix", + "prettier --write" + ] + }, + "dependencies": { + "@arco-design/web-vue": "^2.44.7", + "@vueuse/core": "^9.3.0", + "axios": "^0.24.0", + "bin-wrapper": "npm:bin-wrapper-china", + "dayjs": "^1.11.5", + "echarts": "^5.4.0", + "lodash": "^4.17.21", + "mitt": "^3.0.0", + "nprogress": "^0.2.0", + "pinia": "^2.0.23", + "query-string": "^8.0.3", + "sortablejs": "^1.15.0", + "vue": "^3.2.40", + "vue-echarts": "^6.2.3", + "vue-i18n": "^9.2.2", + "vue-router": "^4.0.14" + }, + "devDependencies": { + "@arco-plugins/vite-vue": "^1.4.5", + "@commitlint/cli": "^17.1.2", + "@commitlint/config-conventional": "^17.1.0", + "@types/lodash": "^4.14.186", + "@types/nprogress": "^0.2.0", + "@types/sortablejs": "^1.15.0", + "@typescript-eslint/eslint-plugin": "^5.40.0", + "@typescript-eslint/parser": "^5.40.0", + "@vitejs/plugin-vue": "^3.1.2", + "@vitejs/plugin-vue-jsx": "^2.0.1", + "@vue/babel-plugin-jsx": "^1.1.1", + "autoprefixer": "^10.4.19", + "consola": "^2.15.3", + "cross-env": "^7.0.3", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-typescript": "^3.5.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-vue": "^9.6.0", + "less": "^4.1.3", + "lint-staged": "^13.0.3", + "postcss": "^8.4.38", + "postcss-html": "^1.5.0", + "prettier": "^2.8.8", + "rollup": "^3.9.1", + "rollup-plugin-visualizer": "^5.8.2", + "stylelint": "^14.16.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-rational-order": "^0.1.2", + "stylelint-config-recommended-vue": "^1.4.0", + "stylelint-config-standard": "^29.0.0", + "stylelint-order": "^5.0.0", + "tailwindcss": "^3.4.3", + "typescript": "^4.8.4", + "unplugin-vue-components": "^0.24.1", + "vite": "^3.2.5", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-imagemin": "^0.6.1", + "vite-svg-loader": "^3.6.0", + "vue-tsc": "^1.0.14" + }, + "engines": { + "node": ">=14.0.0" + }, + "resolutions": { + "bin-wrapper": "npm:bin-wrapper-china", + "rollup": "^2.56.3", + "gifsicle": "5.2.0" + } +} diff --git a/passiflora-ui/postcss.config.js b/passiflora-ui/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/passiflora-ui/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/passiflora-ui/src/App.vue b/passiflora-ui/src/App.vue new file mode 100644 index 0000000..bba8c45 --- /dev/null +++ b/passiflora-ui/src/App.vue @@ -0,0 +1,30 @@ + + + diff --git a/passiflora-ui/src/api/dashboard.ts b/passiflora-ui/src/api/dashboard.ts new file mode 100644 index 0000000..e05ea71 --- /dev/null +++ b/passiflora-ui/src/api/dashboard.ts @@ -0,0 +1,4 @@ +export interface ContentDataRecord { + x: string; + y: number; +} diff --git a/passiflora-ui/src/api/interceptor.ts b/passiflora-ui/src/api/interceptor.ts new file mode 100644 index 0000000..c8f6954 --- /dev/null +++ b/passiflora-ui/src/api/interceptor.ts @@ -0,0 +1,56 @@ +import axios from 'axios'; +import type { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { Message, Modal } from '@arco-design/web-vue'; +import { useUserStore } from '@/store'; +import { getToken, clearToken } from '@/utils/auth'; +import { Result } from '@/types/global'; +import router from '@/router'; + +if (import.meta.env.VITE_API_BASE_URL) { + axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL; +} + +axios.interceptors.request.use( + (config: AxiosRequestConfig) => { + const token = getToken(); + if (token) { + if (!config.headers) { + config.headers = {}; + } + config.headers.Authorization = `${token}`; + } + return config; + }, + (error) => { + // do something + return Promise.reject(error); + } +); +// add response interceptors +axios.interceptors.response.use( + async (response: AxiosResponse) => { + const res = response.data; + // if the custom code is not 20000, it is judged as an error. + if (res.code !== 200) { + // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; + if (res.code === 401) { + clearToken(); + await router.push({ name: 'notAuth' }); + } else { + Message.error({ + content: res.message || '系统错误', + duration: 5 * 1000, + }); + } + return Promise.reject(new Error(res.message || 'Error')); + } + return response; + }, + (error) => { + Message.error({ + content: error.response?.data?.message || 'Request Error', + duration: 5 * 1000, + }); + return Promise.reject(error); + } +); diff --git a/passiflora-ui/src/api/message.ts b/passiflora-ui/src/api/message.ts new file mode 100644 index 0000000..e2d60ee --- /dev/null +++ b/passiflora-ui/src/api/message.ts @@ -0,0 +1,38 @@ +import axios from 'axios'; + +export interface MessageRecord { + id: number; + type: string; + title: string; + subTitle: string; + avatar?: string; + content: string; + time: string; + status: 0 | 1; + messageType?: number; +} +export type MessageListType = MessageRecord[]; + +export function queryMessageList() { + return axios.post('/api/message/list'); +} + +interface MessageStatus { + ids: number[]; +} + +export function setMessageStatus(data: MessageStatus) { + return axios.post('/api/message/read', data); +} + +export interface ChatRecord { + id: number; + username: string; + content: string; + time: string; + isCollect: boolean; +} + +export function queryChatList() { + return axios.post('/api/chat/list'); +} diff --git a/passiflora-ui/src/api/organization/org.ts b/passiflora-ui/src/api/organization/org.ts new file mode 100644 index 0000000..1572654 --- /dev/null +++ b/passiflora-ui/src/api/organization/org.ts @@ -0,0 +1,47 @@ +import axios from 'axios'; +import qs from 'query-string'; +import { Result, Page, BaseEntity, BasePageParam } from '@/types/global'; + +export type OrgRecord = { + orgId?: string; + orgName?: string; + orgCode?: string; + orgType?: number; + orgLevel?: string; + parentOrgId?: string; + orgIdPath?: string; + children?: OrgRecord[] | undefined; +} & BaseEntity; + +export interface orgPageParams extends Partial, BasePageParam {} + +export function orgPage(params: orgPageParams) { + return axios.get>>('/system-api/sysOrg/page', { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + }); +} + +export function orgAdd(data: OrgRecord) { + return axios.post>('/system-api/sysOrg/add', data); +} + +export function orgUpdate(data: OrgRecord) { + return axios.post>('/system-api/sysOrg/update', data); +} + +export function orgDetail(data: string) { + return axios.post>('/system-api/sysOrg/detail', data); +} + +export function orgDelete(data: string[]) { + return axios.post>('/system-api/sysOrg/delete', data); +} + +export function orgTree(orgName: string) { + return axios.get>( + `/system-api/sysOrg/orgTree?orgName=${orgName}` + ); +} diff --git a/passiflora-ui/src/api/organization/position.ts b/passiflora-ui/src/api/organization/position.ts new file mode 100644 index 0000000..c3e509c --- /dev/null +++ b/passiflora-ui/src/api/organization/position.ts @@ -0,0 +1,80 @@ +import axios from 'axios'; +import qs from 'query-string'; +import { Result, Page, BaseEntity, BasePageParam } from '@/types/global'; + +export type PositionRecord = { + positionId?: string; + positionName?: string; + positionLevel?: string; + parentPositionId?: string; + positionIdPath?: string; + dataScopeType?: number; + positionStatus?: number; + order?: number; + children?: PositionRecord[] | undefined; +} & BaseEntity; + +export interface positionPageParams + extends Partial, + BasePageParam {} + +export function positionPage(params: positionPageParams) { + return axios.get>>( + '/system-api/sysPosition/page', + { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + } + ); +} + +export function positionAdd(data: PositionRecord) { + return axios.post>('/system-api/sysPosition/add', data); +} + +export function positionUpdate(data: PositionRecord) { + return axios.post>('/system-api/sysPosition/update', data); +} + +export function positionDetail(data: string) { + return axios.post>( + '/system-api/sysPosition/detail', + data + ); +} + +export function positionDelete(data: string[]) { + return axios.post>( + '/system-api/sysPosition/delete', + data + ); +} + +export function positionTree(positionName: string) { + return axios.get>( + `/system-api/sysPosition/positionTree?positionName=${positionName}` + ); +} + +export function positionUpdateOrder(data: PositionRecord[]) { + return axios.post>( + '/system-api/sysPosition/updateOrder', + data + ); +} + +export function positionDisable(data: string[]) { + return axios.post>( + '/system-api/sysPosition/disable', + data + ); +} + +export function positionEnable(data: string[]) { + return axios.post>( + '/system-api/sysPosition/enable', + data + ); +} diff --git a/passiflora-ui/src/api/organization/user.ts b/passiflora-ui/src/api/organization/user.ts new file mode 100644 index 0000000..d7b8e82 --- /dev/null +++ b/passiflora-ui/src/api/organization/user.ts @@ -0,0 +1,78 @@ +import axios from 'axios'; +import qs from 'query-string'; +import type { RouteRecordNormalized } from 'vue-router'; +import { UserState } from '@/store/modules/user/types'; +import { Result, Page, BaseEntity, BasePageParam } from '@/types/global'; +import { OrgRecord } from '@/api/organization/org'; + +export interface LoginData { + userName: string; + userPassword: string; +} + +export type UserRecord = { + userId?: string; + realName?: string; + userName?: string; + idCardNo?: string; + dateOfBirth?: string; + gender?: 0 | 1; + phoneNum?: string; + email?: string; + remark?: string; + userPassword?: string; + salt?: string; + avatarFile?: string; + orgId?: string; + orgName?: string; +} & BaseEntity; + +export interface userPageParams extends Partial, BasePageParam {} + +export interface UserPage { + list: UserRecord[]; + total: number; +} + +export function userPage(params: userPageParams) { + return axios.get>>('/system-api/sysUser/page', { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + }); +} + +export function userAdd(data: UserRecord) { + return axios.post>('/system-api/sysUser/add', data); +} + +export function login(data: LoginData) { + return axios.post>('/system-api/sysUser/login', data); +} + +export function logout() { + return axios.get>('/system-api/sysUser/logout'); +} + +export function currentUserInfo() { + return axios.get>('/system-api/sysUser/currentUserInfo'); +} + +export function userUpdate(data: OrgRecord) { + return axios.post>('/system-api/sysUser/update', data); +} + +export function userDetail(data: string) { + return axios.post>('/system-api/sysUser/detail', data); +} + +export function userDelete(data: string[]) { + return axios.post>('/system-api/sysUser/delete', data); +} + +export function getMenuList() { + return axios.get>( + '/system-api/sysPermission/menuTree' + ); +} diff --git a/passiflora-ui/src/api/system/dict.ts b/passiflora-ui/src/api/system/dict.ts new file mode 100644 index 0000000..fdba450 --- /dev/null +++ b/passiflora-ui/src/api/system/dict.ts @@ -0,0 +1,119 @@ +import axios from 'axios'; +import qs from 'query-string'; +import { Result, Page, BaseEntity, BasePageParam } from '@/types/global'; + +export type DictRecord = { + dictId?: string; + dictName?: string; + dictTag?: string; + remark?: string; + isSystem?: 0 | 1; + valueIsOnly?: 0 | 1; +} & BaseEntity; + +export type DictItemRecord = { + dictItemId?: string; + dictId?: string; + label?: string; + value?: string | number; + remark?: string; + isSystem?: 0 | 1; +} & BaseEntity; + +export interface dictPageParams extends Partial, BasePageParam { + [key: string]: any; +} + +export interface dictItemPageParams + extends Partial, + BasePageParam { + [key: string]: any; +} + +export interface DictPage { + list: DictRecord[]; + total: number; +} + +export function dictPage(params: dictPageParams) { + return axios.get>>('/system-api/sysDict/page', { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + }); +} + +export function dictAdd(data: DictRecord) { + return axios.post>('/system-api/sysDict/add', data); +} + +export function dictUpdate(data: DictRecord) { + return axios.post>('/system-api/sysDict/update', data); +} + +export function dictDetail(data: string) { + return axios.post>('/system-api/sysDict/detail', data); +} + +export function dictDelete(data: string[]) { + return axios.post>('/system-api/sysDict/delete', data); +} + +export function dictItemPage(params: dictPageParams) { + return axios.get>>( + '/system-api/sysDictItem/page', + { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + } + ); +} + +export function dictItemListByDictTag(params: string) { + return axios.get>( + `/system-api/sysDictItem/listByDictTag?dictTag=${params}` + ); +} + +export function dictItemAdd(data: DictItemRecord) { + return axios.post>('/system-api/sysDictItem/add', data); +} + +export function dictItemUpdate(data: DictItemRecord) { + return axios.post>('/system-api/sysDictItem/update', data); +} + +export function dictItemDetail(data: string) { + return axios.post>( + '/system-api/sysDictItem/update', + data + ); +} + +export function dictItemDelete(data: string[]) { + return axios.post>('/system-api/sysDictItem/delete', data); +} + +export function listByDictId(data: string) { + return axios.post>( + '/system-api/sysDictItem/listByDictId', + data + ); +} + +export function listByDictName(data: string) { + return axios.post>( + '/system-api/sysDictItem/listByDictName', + data + ); +} + +export function listByDictTag(data: string) { + return axios.post>( + '/system-api/sysDictItem/listByDictTag', + data + ); +} diff --git a/passiflora-ui/src/api/system/enum.ts b/passiflora-ui/src/api/system/enum.ts new file mode 100644 index 0000000..8dc8ed6 --- /dev/null +++ b/passiflora-ui/src/api/system/enum.ts @@ -0,0 +1,11 @@ +import axios from 'axios'; +import { Result } from '@/types/global'; + +export type EnumRecord = { + label: string; + value: number | string; +}; + +export function list(param: string) { + return axios.get>(`/system-api/enum/${param}`); +} diff --git a/passiflora-ui/src/api/system/permission.ts b/passiflora-ui/src/api/system/permission.ts new file mode 100644 index 0000000..deb5d3f --- /dev/null +++ b/passiflora-ui/src/api/system/permission.ts @@ -0,0 +1,84 @@ +import axios from 'axios'; +import qs from 'query-string'; +import { Result, Page, BaseEntity, BasePageParam } from '@/types/global'; + +export type PermissionRecord = { + permissionId?: string; + permissionTitle?: string; + permissionName?: string; + permissionIcon?: string; + permissionParentId?: string; + permissionPath?: string; + order?: number; + remark?: string; + permissionLevel?: number; + permissionStatus?: number; + permissionType?: number; + children?: PermissionRecord[] | undefined; + [key: string]: unknown; +} & BaseEntity; + +export interface permissionPageParams + extends Partial, + BasePageParam {} + +export function menuPage(params: permissionPageParams) { + return axios.get>>( + '/system-api/sysPermission/page', + { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + } + ); +} + +export function permissionTableTree() { + return axios.get>( + '/system-api/sysPermission/permissionTableTree' + ); +} + +export function menuUpdateOrder(data: PermissionRecord[]) { + return axios.post>( + '/system-api/sysPermission/updateOrder', + data + ); +} + +export function menuAdd(data: PermissionRecord) { + return axios.post>('/system-api/sysPermission/add', data); +} + +export function menuUpdate(data: PermissionRecord) { + return axios.post>('/system-api/sysPermission/update', data); +} + +export function menuDetail(data: string) { + return axios.post>( + '/system-api/sysPermission/detail', + data + ); +} + +export function menuDelete(data: string[]) { + return axios.post>( + '/system-api/sysPermission/delete', + data + ); +} + +export function menuDisable(data: string[]) { + return axios.post>( + '/system-api/sysPermission/disable', + data + ); +} + +export function menuEnable(data: string[]) { + return axios.post>( + '/system-api/sysPermission/enable', + data + ); +} diff --git a/passiflora-ui/src/assets/images/login-banner.png b/passiflora-ui/src/assets/images/login-banner.png new file mode 100644 index 0000000..a3b3776 Binary files /dev/null and b/passiflora-ui/src/assets/images/login-banner.png differ diff --git a/passiflora-ui/src/assets/logo.svg b/passiflora-ui/src/assets/logo.svg new file mode 100644 index 0000000..3215438 --- /dev/null +++ b/passiflora-ui/src/assets/logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/passiflora-ui/src/assets/style/breakpoint.less b/passiflora-ui/src/assets/style/breakpoint.less new file mode 100644 index 0000000..584f21e --- /dev/null +++ b/passiflora-ui/src/assets/style/breakpoint.less @@ -0,0 +1,19 @@ +// ==============breakpoint============ + +// Extra small screen / phone +@screen-xs: 480px; + +// Small screen / tablet +@screen-sm: 576px; + +// Medium screen / desktop +@screen-md: 768px; + +// Large screen / wide desktop +@screen-lg: 992px; + +// Extra large screen / full hd +@screen-xl: 1200px; + +// Extra extra large screen / large desktop +@screen-xxl: 1600px; diff --git a/passiflora-ui/src/assets/style/global.less b/passiflora-ui/src/assets/style/global.less new file mode 100644 index 0000000..5f7ece4 --- /dev/null +++ b/passiflora-ui/src/assets/style/global.less @@ -0,0 +1,107 @@ +@tailwind base; +@tailwind utilities; + +* { + box-sizing: border-box; +} + +html, +body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + font-size: 14px; + background-color: var(--color-bg-1); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +.echarts-tooltip-diy { + background: linear-gradient( + 304.17deg, + rgba(253, 254, 255, 0.6) -6.04%, + rgba(244, 247, 252, 0.6) 85.2% + ) !important; + border: none !important; + backdrop-filter: blur(10px) !important; + /* Note: backdrop-filter has minimal browser support */ + + border-radius: 6px !important; + .content-panel { + display: flex; + justify-content: space-between; + padding: 0 9px; + background: rgba(255, 255, 255, 0.8); + width: 164px; + height: 32px; + line-height: 32px; + box-shadow: 6px 0px 20px rgba(34, 87, 188, 0.1); + border-radius: 4px; + margin-bottom: 4px; + } + .tooltip-title { + margin: 0 0 10px 0; + } + p { + margin: 0; + } + .tooltip-title, + .tooltip-value { + font-size: 13px; + line-height: 15px; + display: flex; + align-items: center; + text-align: right; + color: #1d2129; + font-weight: bold; + } + .tooltip-item-icon { + display: inline-block; + margin-right: 8px; + width: 10px; + height: 10px; + border-radius: 50%; + } +} + +.general-card { + border-radius: 4px; + border: none; + & > .arco-card-header { + height: auto; + padding: 20px; + border: none; + } + & > .arco-card-body { + padding: 0 20px 20px 20px; + } +} + +.split-line { + border-color: rgb(var(--gray-2)); +} + +.arco-table-cell { + .circle { + display: inline-block; + margin-right: 4px; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: rgb(var(--blue-6)); + &.pass { + background-color: rgb(var(--green-6)); + } + &.warn { + background-color: rgb(var(--orange-6)); + } + &.err { + background-color: rgb(var(--red-6)); + } + } +} + +.inner-container { + height: calc(100% - 16px); +} diff --git a/passiflora-ui/src/assets/world.json b/passiflora-ui/src/assets/world.json new file mode 100644 index 0000000..2d59731 --- /dev/null +++ b/passiflora-ui/src/assets/world.json @@ -0,0 +1 @@ +{"type":"FeatureCollection","features":[{"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[62.230651,35.270664],[62.984662,35.404041],[63.193538,35.857166],[63.982896,36.007957],[64.546479,36.312073],[64.746105,37.111818],[65.588948,37.305217],[65.745631,37.661164],[66.217385,37.39379],[66.518607,37.362784],[67.075782,37.356144],[67.83,37.144994],[68.135562,37.023115],[68.859446,37.344336],[69.196273,37.151144],[69.518785,37.608997],[70.116578,37.588223],[70.270574,37.735165],[70.376304,38.138396],[70.806821,38.486282],[71.348131,38.258905],[71.239404,37.953265],[71.541918,37.905774],[71.448693,37.065645],[71.844638,36.738171],[72.193041,36.948288],[72.63689,37.047558],[73.260056,37.495257],[73.948696,37.421566],[74.980002,37.41999],[75.158028,37.133031],[74.575893,37.020841],[74.067552,36.836176],[72.920025,36.720007],[71.846292,36.509942],[71.262348,36.074388],[71.498768,35.650563],[71.613076,35.153203],[71.115019,34.733126],[71.156773,34.348911],[70.881803,33.988856],[69.930543,34.02012],[70.323594,33.358533],[69.687147,33.105499],[69.262522,32.501944],[69.317764,31.901412],[68.926677,31.620189],[68.556932,31.71331],[67.792689,31.58293],[67.683394,31.303154],[66.938891,31.304911],[66.381458,30.738899],[66.346473,29.887943],[65.046862,29.472181],[64.350419,29.560031],[64.148002,29.340819],[63.550261,29.468331],[62.549857,29.318572],[60.874248,29.829239],[61.781222,30.73585],[61.699314,31.379506],[60.941945,31.548075],[60.863655,32.18292],[60.536078,32.981269],[60.9637,33.528832],[60.52843,33.676446],[60.803193,34.404102],[61.210817,35.650072]]]},"type":"Feature","id":"AFG","properties":{"name":"Afghanistan","id":"AFG","centroid":[69.11,34.28]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[16.326528,-5.87747],[16.57318,-6.622645],[16.860191,-7.222298],[17.089996,-7.545689],[17.47297,-8.068551],[18.134222,-7.987678],[18.464176,-7.847014],[19.016752,-7.988246],[19.166613,-7.738184],[19.417502,-7.155429],[20.037723,-7.116361],[20.091622,-6.94309],[20.601823,-6.939318],[20.514748,-7.299606],[21.728111,-7.290872],[21.746456,-7.920085],[21.949131,-8.305901],[21.801801,-8.908707],[21.875182,-9.523708],[22.208753,-9.894796],[22.155268,-11.084801],[22.402798,-10.993075],[22.837345,-11.017622],[23.456791,-10.867863],[23.912215,-10.926826],[24.017894,-11.237298],[23.904154,-11.722282],[24.079905,-12.191297],[23.930922,-12.565848],[24.016137,-12.911046],[21.933886,-12.898437],[21.887843,-16.08031],[22.562478,-16.898451],[23.215048,-17.523116],[21.377176,-17.930636],[18.956187,-17.789095],[18.263309,-17.309951],[14.209707,-17.353101],[14.058501,-17.423381],[13.462362,-16.971212],[12.814081,-16.941343],[12.215461,-17.111668],[11.734199,-17.301889],[11.640096,-16.673142],[11.778537,-15.793816],[12.123581,-14.878316],[12.175619,-14.449144],[12.500095,-13.5477],[12.738479,-13.137906],[13.312914,-12.48363],[13.633721,-12.038645],[13.738728,-11.297863],[13.686379,-10.731076],[13.387328,-10.373578],[13.120988,-9.766897],[12.87537,-9.166934],[12.929061,-8.959091],[13.236433,-8.562629],[12.93304,-7.596539],[12.728298,-6.927122],[12.227347,-6.294448],[12.322432,-6.100092],[12.735171,-5.965682],[13.024869,-5.984389],[13.375597,-5.864241],[16.326528,-5.87747]]],[[[12.436688,-5.684304],[12.182337,-5.789931],[11.914963,-5.037987],[12.318608,-4.60623],[12.62076,-4.438023],[12.995517,-4.781103],[12.631612,-4.991271],[12.468004,-5.248362],[12.436688,-5.684304]]]]},"type":"Feature","id":"AGO","properties":{"name":"Angola","id":"AGO","centroid":[13.15,-8.5]}},{"geometry":{"type":"Polygon","coordinates":[[[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855404]]]},"type":"Feature","id":"ALB","properties":{"name":"Albania","id":"ALB","centroid":[19.49,41.18]}},{"geometry":{"type":"Polygon","coordinates":[[[51.579519,24.245497],[51.757441,24.294073],[51.794389,24.019826],[52.577081,24.177439],[53.404007,24.151317],[54.008001,24.121758],[54.693024,24.797892],[55.439025,25.439145],[56.070821,26.055464],[56.261042,25.714606],[56.396847,24.924732],[55.886233,24.920831],[55.804119,24.269604],[55.981214,24.130543],[55.528632,23.933604],[55.525841,23.524869],[55.234489,23.110993],[55.208341,22.70833],[55.006803,22.496948],[52.000733,23.001154],[51.617708,24.014219],[51.579519,24.245497]]]},"type":"Feature","id":"ARE","properties":{"name":"United Arab Emirates","id":"ARE","centroid":[55.18,25.15]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-65.5,-55.2],[-66.45,-55.25],[-66.95992,-54.89681],[-67.56244,-54.87001],[-68.63335,-54.8695],[-68.63401,-52.63637],[-68.25,-53.1],[-67.75,-53.85],[-66.45,-54.45],[-65.05,-54.7],[-65.5,-55.2]]],[[[-64.964892,-22.075862],[-64.377021,-22.798091],[-63.986838,-21.993644],[-62.846468,-22.034985],[-62.685057,-22.249029],[-60.846565,-23.880713],[-60.028966,-24.032796],[-58.807128,-24.771459],[-57.777217,-25.16234],[-57.63366,-25.603657],[-58.618174,-27.123719],[-57.60976,-27.395899],[-56.486702,-27.548499],[-55.695846,-27.387837],[-54.788795,-26.621786],[-54.625291,-25.739255],[-54.13005,-25.547639],[-53.628349,-26.124865],[-53.648735,-26.923473],[-54.490725,-27.474757],[-55.162286,-27.881915],[-56.2909,-28.852761],[-57.625133,-30.216295],[-57.874937,-31.016556],[-58.14244,-32.044504],[-58.132648,-33.040567],[-58.349611,-33.263189],[-58.427074,-33.909454],[-58.495442,-34.43149],[-57.22583,-35.288027],[-57.362359,-35.97739],[-56.737487,-36.413126],[-56.788285,-36.901572],[-57.749157,-38.183871],[-59.231857,-38.72022],[-61.237445,-38.928425],[-62.335957,-38.827707],[-62.125763,-39.424105],[-62.330531,-40.172586],[-62.145994,-40.676897],[-62.745803,-41.028761],[-63.770495,-41.166789],[-64.73209,-40.802677],[-65.118035,-41.064315],[-64.978561,-42.058001],[-64.303408,-42.359016],[-63.755948,-42.043687],[-63.458059,-42.563138],[-64.378804,-42.873558],[-65.181804,-43.495381],[-65.328823,-44.501366],[-65.565269,-45.036786],[-66.509966,-45.039628],[-67.293794,-45.551896],[-67.580546,-46.301773],[-66.597066,-47.033925],[-65.641027,-47.236135],[-65.985088,-48.133289],[-67.166179,-48.697337],[-67.816088,-49.869669],[-68.728745,-50.264218],[-69.138539,-50.73251],[-68.815561,-51.771104],[-68.149995,-52.349983],[-68.571545,-52.299444],[-69.498362,-52.142761],[-71.914804,-52.009022],[-72.329404,-51.425956],[-72.309974,-50.67701],[-72.975747,-50.74145],[-73.328051,-50.378785],[-73.415436,-49.318436],[-72.648247,-48.878618],[-72.331161,-48.244238],[-72.447355,-47.738533],[-71.917258,-46.884838],[-71.552009,-45.560733],[-71.659316,-44.973689],[-71.222779,-44.784243],[-71.329801,-44.407522],[-71.793623,-44.207172],[-71.464056,-43.787611],[-71.915424,-43.408565],[-72.148898,-42.254888],[-71.746804,-42.051386],[-71.915734,-40.832339],[-71.680761,-39.808164],[-71.413517,-38.916022],[-70.814664,-38.552995],[-71.118625,-37.576827],[-71.121881,-36.658124],[-70.364769,-36.005089],[-70.388049,-35.169688],[-69.817309,-34.193571],[-69.814777,-33.273886],[-70.074399,-33.09121],[-70.535069,-31.36501],[-69.919008,-30.336339],[-70.01355,-29.367923],[-69.65613,-28.459141],[-69.001235,-27.521214],[-68.295542,-26.89934],[-68.5948,-26.506909],[-68.386001,-26.185016],[-68.417653,-24.518555],[-67.328443,-24.025303],[-66.985234,-22.986349],[-67.106674,-22.735925],[-66.273339,-21.83231],[-64.964892,-22.075862]]]]},"type":"Feature","id":"ARG","properties":{"name":"Argentina","id":"ARG","centroid":[-60,-36.3]}},{"geometry":{"type":"Polygon","coordinates":[[[43.582746,41.092143],[44.97248,41.248129],[45.179496,40.985354],[45.560351,40.81229],[45.359175,40.561504],[45.891907,40.218476],[45.610012,39.899994],[46.034534,39.628021],[46.483499,39.464155],[46.50572,38.770605],[46.143623,38.741201],[45.735379,39.319719],[45.739978,39.473999],[45.298145,39.471751],[45.001987,39.740004],[44.79399,39.713003],[44.400009,40.005],[43.656436,40.253564],[43.752658,40.740201],[43.582746,41.092143]]]},"type":"Feature","id":"ARM","properties":{"name":"Armenia","id":"ARM","centroid":[44.31,40.1]}},{"geometry":{"type":"Polygon","coordinates":[[[68.935,-48.625],[69.58,-48.94],[70.525,-49.065],[70.56,-49.255],[70.28,-49.71],[68.745,-49.775],[68.72,-49.2425],[68.8675,-48.83],[68.935,-48.625]]]},"type":"Feature","id":"ATF","properties":{"name":"French Southern and Antarctic Lands","id":"ATF","centroid":null}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[145.397978,-40.792549],[146.364121,-41.137695],[146.908584,-41.000546],[147.689259,-40.808258],[148.289068,-40.875438],[148.359865,-42.062445],[148.017301,-42.407024],[147.914052,-43.211522],[147.564564,-42.937689],[146.870343,-43.634597],[146.663327,-43.580854],[146.048378,-43.549745],[145.43193,-42.693776],[145.29509,-42.03361],[144.718071,-41.162552],[144.743755,-40.703975],[145.397978,-40.792549]]],[[[143.561811,-13.763656],[143.922099,-14.548311],[144.563714,-14.171176],[144.894908,-14.594458],[145.374724,-14.984976],[145.271991,-15.428205],[145.48526,-16.285672],[145.637033,-16.784918],[145.888904,-16.906926],[146.160309,-17.761655],[146.063674,-18.280073],[146.387478,-18.958274],[147.471082,-19.480723],[148.177602,-19.955939],[148.848414,-20.39121],[148.717465,-20.633469],[149.28942,-21.260511],[149.678337,-22.342512],[150.077382,-22.122784],[150.482939,-22.556142],[150.727265,-22.402405],[150.899554,-23.462237],[151.609175,-24.076256],[152.07354,-24.457887],[152.855197,-25.267501],[153.136162,-26.071173],[153.161949,-26.641319],[153.092909,-27.2603],[153.569469,-28.110067],[153.512108,-28.995077],[153.339095,-29.458202],[153.069241,-30.35024],[153.089602,-30.923642],[152.891578,-31.640446],[152.450002,-32.550003],[151.709117,-33.041342],[151.343972,-33.816023],[151.010555,-34.31036],[150.714139,-35.17346],[150.32822,-35.671879],[150.075212,-36.420206],[149.946124,-37.109052],[149.997284,-37.425261],[149.423882,-37.772681],[148.304622,-37.809061],[147.381733,-38.219217],[146.922123,-38.606532],[146.317922,-39.035757],[145.489652,-38.593768],[144.876976,-38.417448],[145.032212,-37.896188],[144.485682,-38.085324],[143.609974,-38.809465],[142.745427,-38.538268],[142.17833,-38.380034],[141.606582,-38.308514],[140.638579,-38.019333],[139.992158,-37.402936],[139.806588,-36.643603],[139.574148,-36.138362],[139.082808,-35.732754],[138.120748,-35.612296],[138.449462,-35.127261],[138.207564,-34.384723],[137.71917,-35.076825],[136.829406,-35.260535],[137.352371,-34.707339],[137.503886,-34.130268],[137.890116,-33.640479],[137.810328,-32.900007],[136.996837,-33.752771],[136.372069,-34.094766],[135.989043,-34.890118],[135.208213,-34.47867],[135.239218,-33.947953],[134.613417,-33.222778],[134.085904,-32.848072],[134.273903,-32.617234],[132.990777,-32.011224],[132.288081,-31.982647],[131.326331,-31.495803],[129.535794,-31.590423],[128.240938,-31.948489],[127.102867,-32.282267],[126.148714,-32.215966],[125.088623,-32.728751],[124.221648,-32.959487],[124.028947,-33.483847],[123.659667,-33.890179],[122.811036,-33.914467],[122.183064,-34.003402],[121.299191,-33.821036],[120.580268,-33.930177],[119.893695,-33.976065],[119.298899,-34.509366],[119.007341,-34.464149],[118.505718,-34.746819],[118.024972,-35.064733],[117.295507,-35.025459],[116.625109,-35.025097],[115.564347,-34.386428],[115.026809,-34.196517],[115.048616,-33.623425],[115.545123,-33.487258],[115.714674,-33.259572],[115.679379,-32.900369],[115.801645,-32.205062],[115.689611,-31.612437],[115.160909,-30.601594],[114.997043,-30.030725],[115.040038,-29.461095],[114.641974,-28.810231],[114.616498,-28.516399],[114.173579,-28.118077],[114.048884,-27.334765],[113.477498,-26.543134],[113.338953,-26.116545],[113.778358,-26.549025],[113.440962,-25.621278],[113.936901,-25.911235],[114.232852,-26.298446],[114.216161,-25.786281],[113.721255,-24.998939],[113.625344,-24.683971],[113.393523,-24.384764],[113.502044,-23.80635],[113.706993,-23.560215],[113.843418,-23.059987],[113.736552,-22.475475],[114.149756,-21.755881],[114.225307,-22.517488],[114.647762,-21.82952],[115.460167,-21.495173],[115.947373,-21.068688],[116.711615,-20.701682],[117.166316,-20.623599],[117.441545,-20.746899],[118.229559,-20.374208],[118.836085,-20.263311],[118.987807,-20.044203],[119.252494,-19.952942],[119.805225,-19.976506],[120.85622,-19.683708],[121.399856,-19.239756],[121.655138,-18.705318],[122.241665,-18.197649],[122.286624,-17.798603],[122.312772,-17.254967],[123.012574,-16.4052],[123.433789,-17.268558],[123.859345,-17.069035],[123.503242,-16.596506],[123.817073,-16.111316],[124.258287,-16.327944],[124.379726,-15.56706],[124.926153,-15.0751],[125.167275,-14.680396],[125.670087,-14.51007],[125.685796,-14.230656],[126.125149,-14.347341],[126.142823,-14.095987],[126.582589,-13.952791],[127.065867,-13.817968],[127.804633,-14.276906],[128.35969,-14.86917],[128.985543,-14.875991],[129.621473,-14.969784],[129.4096,-14.42067],[129.888641,-13.618703],[130.339466,-13.357376],[130.183506,-13.10752],[130.617795,-12.536392],[131.223495,-12.183649],[131.735091,-12.302453],[132.575298,-12.114041],[132.557212,-11.603012],[131.824698,-11.273782],[132.357224,-11.128519],[133.019561,-11.376411],[133.550846,-11.786515],[134.393068,-12.042365],[134.678632,-11.941183],[135.298491,-12.248606],[135.882693,-11.962267],[136.258381,-12.049342],[136.492475,-11.857209],[136.95162,-12.351959],[136.685125,-12.887223],[136.305407,-13.29123],[135.961758,-13.324509],[136.077617,-13.724278],[135.783836,-14.223989],[135.428664,-14.715432],[135.500184,-14.997741],[136.295175,-15.550265],[137.06536,-15.870762],[137.580471,-16.215082],[138.303217,-16.807604],[138.585164,-16.806622],[139.108543,-17.062679],[139.260575,-17.371601],[140.215245,-17.710805],[140.875463,-17.369069],[141.07111,-16.832047],[141.274095,-16.38887],[141.398222,-15.840532],[141.702183,-15.044921],[141.56338,-14.561333],[141.63552,-14.270395],[141.519869,-13.698078],[141.65092,-12.944688],[141.842691,-12.741548],[141.68699,-12.407614],[141.928629,-11.877466],[142.118488,-11.328042],[142.143706,-11.042737],[142.51526,-10.668186],[142.79731,-11.157355],[142.866763,-11.784707],[143.115947,-11.90563],[143.158632,-12.325656],[143.522124,-12.834358],[143.597158,-13.400422],[143.561811,-13.763656]]]]},"type":"Feature","id":"AUS","properties":{"name":"Australia","id":"AUS","centroid":[149.08,-35.15]}},{"geometry":{"type":"Polygon","coordinates":[[[16.979667,48.123497],[16.903754,47.714866],[16.340584,47.712902],[16.534268,47.496171],[16.202298,46.852386],[16.011664,46.683611],[15.137092,46.658703],[14.632472,46.431817],[13.806475,46.509306],[12.376485,46.767559],[12.153088,47.115393],[11.164828,46.941579],[11.048556,46.751359],[10.442701,46.893546],[9.932448,46.920728],[9.47997,47.10281],[9.632932,47.347601],[9.594226,47.525058],[9.896068,47.580197],[10.402084,47.302488],[10.544504,47.566399],[11.426414,47.523766],[12.141357,47.703083],[12.62076,47.672388],[12.932627,47.467646],[13.025851,47.637584],[12.884103,48.289146],[13.243357,48.416115],[13.595946,48.877172],[14.338898,48.555305],[14.901447,48.964402],[15.253416,49.039074],[16.029647,48.733899],[16.499283,48.785808],[16.960288,48.596982],[16.879983,48.470013],[16.979667,48.123497]]]},"type":"Feature","id":"AUT","properties":{"name":"Austria","id":"AUT","centroid":[16.22,48.12]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[45.001987,39.740004],[45.298145,39.471751],[45.739978,39.473999],[45.735379,39.319719],[46.143623,38.741201],[45.457722,38.874139],[44.952688,39.335765],[44.79399,39.713003],[45.001987,39.740004]]],[[[47.373315,41.219732],[47.815666,41.151416],[47.987283,41.405819],[48.584353,41.80887],[49.110264,41.282287],[49.618915,40.572924],[50.08483,40.526157],[50.392821,40.256561],[49.569202,40.176101],[49.395259,39.399482],[49.223228,39.049219],[48.856532,38.815486],[48.883249,38.320245],[48.634375,38.270378],[48.010744,38.794015],[48.355529,39.288765],[48.060095,39.582235],[47.685079,39.508364],[46.50572,38.770605],[46.483499,39.464155],[46.034534,39.628021],[45.610012,39.899994],[45.891907,40.218476],[45.359175,40.561504],[45.560351,40.81229],[45.179496,40.985354],[44.97248,41.248129],[45.217426,41.411452],[45.962601,41.123873],[46.501637,41.064445],[46.637908,41.181673],[46.145432,41.722802],[46.404951,41.860675],[46.686071,41.827137],[47.373315,41.219732]]]]},"type":"Feature","id":"AZE","properties":{"name":"Azerbaijan","id":"AZE","centroid":[49.56,40.29]}},{"geometry":{"type":"Polygon","coordinates":[[[29.339998,-4.499983],[29.276384,-3.293907],[29.024926,-2.839258],[29.632176,-2.917858],[29.938359,-2.348487],[30.469696,-2.413858],[30.527677,-2.807632],[30.743013,-3.034285],[30.752263,-3.35933],[30.50556,-3.568567],[30.116333,-4.090138],[29.753512,-4.452389],[29.339998,-4.499983]]]},"type":"Feature","id":"BDI","properties":{"name":"Burundi","id":"BDI","centroid":[29.18,-3.16]}},{"geometry":{"type":"Polygon","coordinates":[[[3.314971,51.345781],[4.047071,51.267259],[4.973991,51.475024],[5.606976,51.037298],[6.156658,50.803721],[6.043073,50.128052],[5.782417,50.090328],[5.674052,49.529484],[4.799222,49.985373],[4.286023,49.907497],[3.588184,50.378992],[3.123252,50.780363],[2.658422,50.796848],[2.513573,51.148506],[3.314971,51.345781]]]},"type":"Feature","id":"BEL","properties":{"name":"Belgium","id":"BEL","centroid":[4.21,50.51]}},{"geometry":{"type":"Polygon","coordinates":[[[2.691702,6.258817],[1.865241,6.142158],[1.618951,6.832038],[1.664478,9.12859],[1.463043,9.334624],[1.425061,9.825395],[1.077795,10.175607],[0.772336,10.470808],[0.899563,10.997339],[1.24347,11.110511],[1.447178,11.547719],[1.935986,11.64115],[2.154474,11.94015],[2.490164,12.233052],[2.848643,12.235636],[3.61118,11.660167],[3.572216,11.327939],[3.797112,10.734746],[3.60007,10.332186],[3.705438,10.06321],[3.220352,9.444153],[2.912308,9.137608],[2.723793,8.506845],[2.749063,7.870734],[2.691702,6.258817]]]},"type":"Feature","id":"BEN","properties":{"name":"Benin","id":"BEN","centroid":[2.42,6.23]}},{"geometry":{"type":"Polygon","coordinates":[[[-2.827496,9.642461],[-3.511899,9.900326],[-3.980449,9.862344],[-4.330247,9.610835],[-4.779884,9.821985],[-4.954653,10.152714],[-5.404342,10.370737],[-5.470565,10.95127],[-5.197843,11.375146],[-5.220942,11.713859],[-4.427166,12.542646],[-4.280405,13.228444],[-4.006391,13.472485],[-3.522803,13.337662],[-3.103707,13.541267],[-2.967694,13.79815],[-2.191825,14.246418],[-2.001035,14.559008],[-1.066363,14.973815],[-0.515854,15.116158],[-0.266257,14.924309],[0.374892,14.928908],[0.295646,14.444235],[0.429928,13.988733],[0.993046,13.33575],[1.024103,12.851826],[2.177108,12.625018],[2.154474,11.94015],[1.935986,11.64115],[1.447178,11.547719],[1.24347,11.110511],[0.899563,10.997339],[0.023803,11.018682],[-0.438702,11.098341],[-0.761576,10.93693],[-1.203358,11.009819],[-2.940409,10.96269],[-2.963896,10.395335],[-2.827496,9.642461]]]},"type":"Feature","id":"BFA","properties":{"name":"Burkina Faso","id":"BFA","centroid":[-1.3,12.15]}},{"geometry":{"type":"Polygon","coordinates":[[[92.672721,22.041239],[92.652257,21.324048],[92.303234,21.475485],[92.368554,20.670883],[92.082886,21.192195],[92.025215,21.70157],[91.834891,22.182936],[91.417087,22.765019],[90.496006,22.805017],[90.586957,22.392794],[90.272971,21.836368],[89.847467,22.039146],[89.70205,21.857116],[89.418863,21.966179],[89.031961,22.055708],[88.876312,22.879146],[88.52977,23.631142],[88.69994,24.233715],[88.084422,24.501657],[88.306373,24.866079],[88.931554,25.238692],[88.209789,25.768066],[88.563049,26.446526],[89.355094,26.014407],[89.832481,25.965082],[89.920693,25.26975],[90.872211,25.132601],[91.799596,25.147432],[92.376202,24.976693],[91.915093,24.130414],[91.46773,24.072639],[91.158963,23.503527],[91.706475,22.985264],[91.869928,23.624346],[92.146035,23.627499],[92.672721,22.041239]]]},"type":"Feature","id":"BGD","properties":{"name":"Bangladesh","id":"BGD","centroid":[90.23,23.42]}},{"geometry":{"type":"Polygon","coordinates":[[[22.65715,44.234923],[22.944832,43.823785],[23.332302,43.897011],[24.100679,43.741051],[25.569272,43.688445],[26.065159,43.943494],[27.2424,44.175986],[27.970107,43.812468],[28.558081,43.707462],[28.039095,43.293172],[27.673898,42.577892],[27.99672,42.007359],[27.135739,42.141485],[26.117042,41.826905],[26.106138,41.328899],[25.197201,41.234486],[24.492645,41.583896],[23.692074,41.309081],[22.952377,41.337994],[22.881374,41.999297],[22.380526,42.32026],[22.545012,42.461362],[22.436595,42.580321],[22.604801,42.898519],[22.986019,43.211161],[22.500157,43.642814],[22.410446,44.008063],[22.65715,44.234923]]]},"type":"Feature","id":"BGR","properties":{"name":"Bulgaria","id":"BGR","centroid":[23.2,42.45]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-77.53466,23.75975],[-77.78,23.71],[-78.03405,24.28615],[-78.40848,24.57564],[-78.19087,25.2103],[-77.89,25.17],[-77.54,24.34],[-77.53466,23.75975]]],[[[-77.82,26.58],[-78.91,26.42],[-78.98,26.79],[-78.51,26.87],[-77.85,26.84],[-77.82,26.58]]],[[[-77,26.59],[-77.17255,25.87918],[-77.35641,26.00735],[-77.34,26.53],[-77.78802,26.92516],[-77.79,27.04],[-77,26.59]]]]},"type":"Feature","id":"BHS","properties":{"name":"The Bahamas","id":"BHS","centroid":[-77.2,25.05]}},{"geometry":{"type":"Polygon","coordinates":[[[19.005486,44.860234],[19.36803,44.863],[19.11761,44.42307],[19.59976,44.03847],[19.454,43.5681],[19.21852,43.52384],[19.03165,43.43253],[18.70648,43.20011],[18.56,42.65],[17.674922,43.028563],[17.297373,43.446341],[16.916156,43.667722],[16.456443,44.04124],[16.23966,44.351143],[15.750026,44.818712],[15.959367,45.233777],[16.318157,45.004127],[16.534939,45.211608],[17.002146,45.233777],[17.861783,45.06774],[18.553214,45.08159],[19.005486,44.860234]]]},"type":"Feature","id":"BIH","properties":{"name":"Bosnia and Herzegovina","id":"BIH","centroid":[18.25,43.52]}},{"geometry":{"type":"Polygon","coordinates":[[[23.484128,53.912498],[24.450684,53.905702],[25.536354,54.282423],[25.768433,54.846963],[26.588279,55.167176],[26.494331,55.615107],[27.10246,55.783314],[28.176709,56.16913],[29.229513,55.918344],[29.371572,55.670091],[29.896294,55.789463],[30.873909,55.550976],[30.971836,55.081548],[30.757534,54.811771],[31.384472,54.157056],[31.791424,53.974639],[31.731273,53.794029],[32.405599,53.618045],[32.693643,53.351421],[32.304519,53.132726],[31.497644,53.167427],[31.305201,53.073996],[31.540018,52.742052],[31.785998,52.101678],[30.927549,52.042353],[30.619454,51.822806],[30.555117,51.319503],[30.157364,51.416138],[29.254938,51.368234],[28.992835,51.602044],[28.617613,51.427714],[28.241615,51.572227],[27.454066,51.592303],[26.337959,51.832289],[25.327788,51.910656],[24.553106,51.888461],[24.005078,51.617444],[23.527071,51.578454],[23.508002,52.023647],[23.199494,52.486977],[23.799199,52.691099],[23.804935,53.089731],[23.527536,53.470122],[23.484128,53.912498]]]},"type":"Feature","id":"BLR","properties":{"name":"Belarus","id":"BLR","centroid":[27.3,53.52]}},{"geometry":{"type":"Polygon","coordinates":[[[-89.14308,17.808319],[-89.150909,17.955468],[-89.029857,18.001511],[-88.848344,17.883198],[-88.490123,18.486831],[-88.300031,18.499982],[-88.296336,18.353273],[-88.106813,18.348674],[-88.123479,18.076675],[-88.285355,17.644143],[-88.197867,17.489475],[-88.302641,17.131694],[-88.239518,17.036066],[-88.355428,16.530774],[-88.551825,16.265467],[-88.732434,16.233635],[-88.930613,15.887273],[-89.229122,15.886938],[-89.150806,17.015577],[-89.14308,17.808319]]]},"type":"Feature","id":"BLZ","properties":{"name":"Belize","id":"BLZ","centroid":[-88.3,17.18]}},{"geometry":{"type":"Polygon","coordinates":[[[-64.7799734332998,32.3072000581802],[-64.7873319183061,32.3039237143428],[-64.7946942710173,32.3032682700388],[-64.8094297981283,32.3098175728414],[-64.8167896352437,32.3058845718466],[-64.8101968029642,32.3022833180511],[-64.7962291465484,32.2934409732427],[-64.7815086336978,32.2868973114514],[-64.7997025513437,32.2796896417328],[-64.8066707691087,32.2747767569465],[-64.8225587873683,32.2669111289395],[-64.8287548840306,32.2669075473817],[-64.8306732143498,32.2583944840235],[-64.8399924854972,32.254782282336],[-64.8566090462354,32.2547740387514],[-64.8682296789446,32.2616393614322],[-64.8628241459563,32.2724481933959],[-64.8748651338951,32.2757120264753],[-64.8717752856644,32.2819371582026],[-64.8671422127295,32.2930760547989],[-64.8559068764437,32.2960321186471],[-64.8597429072279,32.3015842021933],[-64.8439233486717,32.3140553852543],[-64.8350242329311,32.3242161760006],[-64.8338690593672,32.3294587561557],[-64.8520298651164,32.3110911879954],[-64.8635922932573,32.3048469433363],[-64.8686668994079,32.30910745083],[-64.8721354593415,32.3041908606301],[-64.8779667328485,32.3038632800462],[-64.8780046844321,32.2907757831692],[-64.8849776658292,32.2819261366004],[-64.8783230004629,32.2613001418681],[-64.863194968877,32.2465799485801],[-64.8519819555722,32.2485519134663],[-64.842311980074,32.2492123317296],[-64.8388242605209,32.2475773472534],[-64.8334002575532,32.2462714714698],[-64.8256389530584,32.2472637398594],[-64.8205697556026,32.2531698880328],[-64.8105087275579,32.2561208974156],[-64.7900177727338,32.2659446936992],[-64.7745415970416,32.2718413023427],[-64.7644742436426,32.2855931353214],[-64.7551803442276,32.2908326702531],[-64.7423982971436,32.2996734994024],[-64.7206991797682,32.3137542201258],[-64.7117851247134,32.3176823360806],[-64.6962778813133,32.3275029115532],[-64.6768921127452,32.3324095397555],[-64.6567136927777,32.3451776458469],[-64.6532168823499,32.3494356627941],[-64.6605720384429,32.3589423487763],[-64.65125819471,32.3615600906466],[-64.6462011670816,32.36975169749],[-64.6613227512832,32.3763135008721],[-64.6690666074397,32.388444543924],[-64.6834270548595,32.3854968316788],[-64.6954617672714,32.3763221285869],[-64.70438689565,32.3704254760469],[-64.7117569982798,32.368132600249],[-64.7061764744404,32.3600110593559],[-64.700531552697,32.3590601356818],[-64.6940348033967,32.3640708659835],[-64.6895164826082,32.3633598579866],[-64.6864150099255,32.3547797587266],[-64.6824635995504,32.3540628176846],[-64.6835876652835,32.3626447677968],[-64.6801998697415,32.3631199096979],[-64.6672170444687,32.3597751617473],[-64.6598811264978,32.3497625771755],[-64.6737331235384,32.3390281851635],[-64.6887090648183,32.3342439408053],[-64.706732854446,32.3429010723036],[-64.7149301576112,32.3552188753513],[-64.7185967666669,32.3552239212394],[-64.7214189847314,32.3518830231342],[-64.7270616067222,32.3466461715475],[-64.734962460882,32.3442819830499],[-64.7383521549094,32.3407216514918],[-64.7411729976333,32.3311790864627],[-64.7423019216485,32.323311561213],[-64.7462482354281,32.318538611581],[-64.7566773739613,32.3130509130175],[-64.768738200563,32.3088369816572],[-64.7799734332998,32.3072000581802]]]},"type":"Feature","id":"BMU","properties":{"name":"Bermuda","id":"BMU","centroid":[-64.47,32.18]}},{"geometry":{"type":"Polygon","coordinates":[[[-62.846468,-22.034985],[-63.986838,-21.993644],[-64.377021,-22.798091],[-64.964892,-22.075862],[-66.273339,-21.83231],[-67.106674,-22.735925],[-67.82818,-22.872919],[-68.219913,-21.494347],[-68.757167,-20.372658],[-68.442225,-19.405068],[-68.966818,-18.981683],[-69.100247,-18.260125],[-69.590424,-17.580012],[-68.959635,-16.500698],[-69.389764,-15.660129],[-69.160347,-15.323974],[-69.339535,-14.953195],[-68.948887,-14.453639],[-68.929224,-13.602684],[-68.88008,-12.899729],[-68.66508,-12.5613],[-69.529678,-10.951734],[-68.786158,-11.03638],[-68.271254,-11.014521],[-68.048192,-10.712059],[-67.173801,-10.306812],[-66.646908,-9.931331],[-65.338435,-9.761988],[-65.444837,-10.511451],[-65.321899,-10.895872],[-65.402281,-11.56627],[-64.316353,-12.461978],[-63.196499,-12.627033],[-62.80306,-13.000653],[-62.127081,-13.198781],[-61.713204,-13.489202],[-61.084121,-13.479384],[-60.503304,-13.775955],[-60.459198,-14.354007],[-60.264326,-14.645979],[-60.251149,-15.077219],[-60.542966,-15.09391],[-60.15839,-16.258284],[-58.24122,-16.299573],[-58.388058,-16.877109],[-58.280804,-17.27171],[-57.734558,-17.552468],[-57.498371,-18.174188],[-57.676009,-18.96184],[-57.949997,-19.400004],[-57.853802,-19.969995],[-58.166392,-20.176701],[-58.183471,-19.868399],[-59.115042,-19.356906],[-60.043565,-19.342747],[-61.786326,-19.633737],[-62.265961,-20.513735],[-62.291179,-21.051635],[-62.685057,-22.249029],[-62.846468,-22.034985]]]},"type":"Feature","id":"BOL","properties":{"name":"Bolivia","id":"BOL","centroid":[-68.1,-16.2]}},{"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.2909,-28.852761],[-55.162286,-27.881915],[-54.490725,-27.474757],[-53.648735,-26.923473],[-53.628349,-26.124865],[-54.13005,-25.547639],[-54.625291,-25.739255],[-54.428946,-25.162185],[-54.293476,-24.5708],[-54.29296,-24.021014],[-54.652834,-23.839578],[-55.027902,-24.001274],[-55.400747,-23.956935],[-55.517639,-23.571998],[-55.610683,-22.655619],[-55.797958,-22.35693],[-56.473317,-22.0863],[-56.88151,-22.282154],[-57.937156,-22.090176],[-57.870674,-20.732688],[-58.166392,-20.176701],[-57.853802,-19.969995],[-57.949997,-19.400004],[-57.676009,-18.96184],[-57.498371,-18.174188],[-57.734558,-17.552468],[-58.280804,-17.27171],[-58.388058,-16.877109],[-58.24122,-16.299573],[-60.15839,-16.258284],[-60.542966,-15.09391],[-60.251149,-15.077219],[-60.264326,-14.645979],[-60.459198,-14.354007],[-60.503304,-13.775955],[-61.084121,-13.479384],[-61.713204,-13.489202],[-62.127081,-13.198781],[-62.80306,-13.000653],[-63.196499,-12.627033],[-64.316353,-12.461978],[-65.402281,-11.56627],[-65.321899,-10.895872],[-65.444837,-10.511451],[-65.338435,-9.761988],[-66.646908,-9.931331],[-67.173801,-10.306812],[-68.048192,-10.712059],[-68.271254,-11.014521],[-68.786158,-11.03638],[-69.529678,-10.951734],[-70.093752,-11.123972],[-70.548686,-11.009147],[-70.481894,-9.490118],[-71.302412,-10.079436],[-72.184891,-10.053598],[-72.563033,-9.520194],[-73.226713,-9.462213],[-73.015383,-9.032833],[-73.571059,-8.424447],[-73.987235,-7.52383],[-73.723401,-7.340999],[-73.724487,-6.918595],[-73.120027,-6.629931],[-73.219711,-6.089189],[-72.964507,-5.741251],[-72.891928,-5.274561],[-71.748406,-4.593983],[-70.928843,-4.401591],[-70.794769,-4.251265],[-69.893635,-4.298187],[-69.444102,-1.556287],[-69.420486,-1.122619],[-69.577065,-0.549992],[-70.020656,-0.185156],[-70.015566,0.541414],[-69.452396,0.706159],[-69.252434,0.602651],[-69.218638,0.985677],[-69.804597,1.089081],[-69.816973,1.714805],[-67.868565,1.692455],[-67.53781,2.037163],[-67.259998,1.719999],[-67.065048,1.130112],[-66.876326,1.253361],[-66.325765,0.724452],[-65.548267,0.789254],[-65.354713,1.095282],[-64.611012,1.328731],[-64.199306,1.492855],[-64.083085,1.916369],[-63.368788,2.2009],[-63.422867,2.411068],[-64.269999,2.497006],[-64.408828,3.126786],[-64.368494,3.79721],[-64.816064,4.056445],[-64.628659,4.148481],[-63.888343,4.02053],[-63.093198,3.770571],[-62.804533,4.006965],[-62.08543,4.162124],[-60.966893,4.536468],[-60.601179,4.918098],[-60.733574,5.200277],[-60.213683,5.244486],[-59.980959,5.014061],[-60.111002,4.574967],[-59.767406,4.423503],[-59.53804,3.958803],[-59.815413,3.606499],[-59.974525,2.755233],[-59.718546,2.24963],[-59.646044,1.786894],[-59.030862,1.317698],[-58.540013,1.268088],[-58.429477,1.463942],[-58.11345,1.507195],[-57.660971,1.682585],[-57.335823,1.948538],[-56.782704,1.863711],[-56.539386,1.899523],[-55.995698,1.817667],[-55.9056,2.021996],[-56.073342,2.220795],[-55.973322,2.510364],[-55.569755,2.421506],[-55.097587,2.523748],[-54.524754,2.311849],[-54.088063,2.105557],[-53.778521,2.376703],[-53.554839,2.334897],[-53.418465,2.053389],[-52.939657,2.124858],[-52.556425,2.504705],[-52.249338,3.241094],[-51.657797,4.156232],[-51.317146,4.203491],[-51.069771,3.650398],[-50.508875,1.901564],[-49.974076,1.736483],[-49.947101,1.04619],[-50.699251,0.222984],[-50.388211,-0.078445],[-48.620567,-0.235489],[-48.584497,-1.237805],[-47.824956,-0.581618],[-46.566584,-0.941028],[-44.905703,-1.55174],[-44.417619,-2.13775],[-44.581589,-2.691308],[-43.418791,-2.38311],[-41.472657,-2.912018],[-39.978665,-2.873054],[-38.500383,-3.700652],[-37.223252,-4.820946],[-36.452937,-5.109404],[-35.597796,-5.149504],[-35.235389,-5.464937],[-34.89603,-6.738193],[-34.729993,-7.343221],[-35.128212,-8.996401],[-35.636967,-9.649282],[-37.046519,-11.040721],[-37.683612,-12.171195],[-38.423877,-13.038119],[-38.673887,-13.057652],[-38.953276,-13.79337],[-38.882298,-15.667054],[-39.161092,-17.208407],[-39.267339,-17.867746],[-39.583521,-18.262296],[-39.760823,-19.599113],[-40.774741,-20.904512],[-40.944756,-21.937317],[-41.754164,-22.370676],[-41.988284,-22.97007],[-43.074704,-22.967693],[-44.647812,-23.351959],[-45.352136,-23.796842],[-46.472093,-24.088969],[-47.648972,-24.885199],[-48.495458,-25.877025],[-48.641005,-26.623698],[-48.474736,-27.175912],[-48.66152,-28.186135],[-48.888457,-28.674115],[-49.587329,-29.224469],[-50.696874,-30.984465],[-51.576226,-31.777698],[-52.256081,-32.24537],[-52.7121,-33.196578],[-53.373662,-33.768378],[-53.650544,-33.202004],[-53.209589,-32.727666],[-53.787952,-32.047243],[-54.572452,-31.494511],[-55.60151,-30.853879],[-55.973245,-30.883076],[-56.976026,-30.109686],[-57.625133,-30.216295]]]},"type":"Feature","id":"BRA","properties":{"name":"Brazil","id":"BRA","centroid":[-47.55,-15.47]}},{"geometry":{"type":"Polygon","coordinates":[[[114.204017,4.525874],[114.599961,4.900011],[115.45071,5.44773],[115.4057,4.955228],[115.347461,4.316636],[114.869557,4.348314],[114.659596,4.007637],[114.204017,4.525874]]]},"type":"Feature","id":"BRN","properties":{"name":"Brunei","id":"BRN","centroid":[115,4.52]}},{"geometry":{"type":"Polygon","coordinates":[[[91.696657,27.771742],[92.103712,27.452614],[92.033484,26.83831],[91.217513,26.808648],[90.373275,26.875724],[89.744528,26.719403],[88.835643,27.098966],[88.814248,27.299316],[89.47581,28.042759],[90.015829,28.296439],[90.730514,28.064954],[91.258854,28.040614],[91.696657,27.771742]]]},"type":"Feature","id":"BTN","properties":{"name":"Bhutan","id":"BTN","centroid":[89.45,27.31]}},{"geometry":{"type":"Polygon","coordinates":[[[25.649163,-18.536026],[25.850391,-18.714413],[26.164791,-19.293086],[27.296505,-20.39152],[27.724747,-20.499059],[27.727228,-20.851802],[28.02137,-21.485975],[28.794656,-21.639454],[29.432188,-22.091313],[28.017236,-22.827754],[27.11941,-23.574323],[26.786407,-24.240691],[26.485753,-24.616327],[25.941652,-24.696373],[25.765849,-25.174845],[25.664666,-25.486816],[25.025171,-25.71967],[24.211267,-25.670216],[23.73357,-25.390129],[23.312097,-25.26869],[22.824271,-25.500459],[22.579532,-25.979448],[22.105969,-26.280256],[21.605896,-26.726534],[20.889609,-26.828543],[20.66647,-26.477453],[20.758609,-25.868136],[20.165726,-24.917962],[19.895768,-24.76779],[19.895458,-21.849157],[20.881134,-21.814327],[20.910641,-18.252219],[21.65504,-18.219146],[23.196858,-17.869038],[23.579006,-18.281261],[24.217365,-17.889347],[24.520705,-17.887125],[25.084443,-17.661816],[25.264226,-17.73654],[25.649163,-18.536026]]]},"type":"Feature","id":"BWA","properties":{"name":"Botswana","id":"BWA","centroid":[25.57,-24.45]}},{"geometry":{"type":"Polygon","coordinates":[[[15.27946,7.421925],[16.106232,7.497088],[16.290562,7.754307],[16.456185,7.734774],[16.705988,7.508328],[17.96493,7.890914],[18.389555,8.281304],[18.911022,8.630895],[18.81201,8.982915],[19.094008,9.074847],[20.059685,9.012706],[21.000868,9.475985],[21.723822,10.567056],[22.231129,10.971889],[22.864165,11.142395],[22.977544,10.714463],[23.554304,10.089255],[23.55725,9.681218],[23.394779,9.265068],[23.459013,8.954286],[23.805813,8.666319],[24.567369,8.229188],[25.114932,7.825104],[25.124131,7.500085],[25.796648,6.979316],[26.213418,6.546603],[26.465909,5.946717],[27.213409,5.550953],[27.374226,5.233944],[27.044065,5.127853],[26.402761,5.150875],[25.650455,5.256088],[25.278798,5.170408],[25.128833,4.927245],[24.805029,4.897247],[24.410531,5.108784],[23.297214,4.609693],[22.84148,4.710126],[22.704124,4.633051],[22.405124,4.02916],[21.659123,4.224342],[20.927591,4.322786],[20.290679,4.691678],[19.467784,5.031528],[18.932312,4.709506],[18.542982,4.201785],[18.453065,3.504386],[17.8099,3.560196],[17.133042,3.728197],[16.537058,3.198255],[16.012852,2.26764],[15.907381,2.557389],[15.862732,3.013537],[15.405396,3.335301],[15.03622,3.851367],[14.950953,4.210389],[14.478372,4.732605],[14.558936,5.030598],[14.459407,5.451761],[14.53656,6.226959],[14.776545,6.408498],[15.27946,7.421925]]]},"type":"Feature","id":"CAF","properties":{"name":"Central African Republic","id":"CAF","centroid":[18.33,4.22]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-63.6645,46.55001],[-62.9393,46.41587],[-62.01208,46.44314],[-62.50391,46.03339],[-62.87433,45.96818],[-64.1428,46.39265],[-64.39261,46.72747],[-64.01486,47.03601],[-63.6645,46.55001]]],[[[-61.806305,49.10506],[-62.29318,49.08717],[-63.58926,49.40069],[-64.51912,49.87304],[-64.17322,49.95718],[-62.85829,49.70641],[-61.835585,49.28855],[-61.806305,49.10506]]],[[[-123.510002,48.510011],[-124.012891,48.370846],[-125.655013,48.825005],[-125.954994,49.179996],[-126.850004,49.53],[-127.029993,49.814996],[-128.059336,49.994959],[-128.444584,50.539138],[-128.358414,50.770648],[-127.308581,50.552574],[-126.695001,50.400903],[-125.755007,50.295018],[-125.415002,49.950001],[-124.920768,49.475275],[-123.922509,49.062484],[-123.510002,48.510011]]],[[[-56.134036,50.68701],[-56.795882,49.812309],[-56.143105,50.150117],[-55.471492,49.935815],[-55.822401,49.587129],[-54.935143,49.313011],[-54.473775,49.556691],[-53.476549,49.249139],[-53.786014,48.516781],[-53.086134,48.687804],[-52.958648,48.157164],[-52.648099,47.535548],[-53.069158,46.655499],[-53.521456,46.618292],[-54.178936,46.807066],[-53.961869,47.625207],[-54.240482,47.752279],[-55.400773,46.884994],[-55.997481,46.91972],[-55.291219,47.389562],[-56.250799,47.632545],[-57.325229,47.572807],[-59.266015,47.603348],[-59.419494,47.899454],[-58.796586,48.251525],[-59.231625,48.523188],[-58.391805,49.125581],[-57.35869,50.718274],[-56.73865,51.287438],[-55.870977,51.632094],[-55.406974,51.588273],[-55.600218,51.317075],[-56.134036,50.68701]]],[[[-132.710008,54.040009],[-132.710009,54.040009],[-132.710008,54.040009],[-132.710008,54.040009],[-131.74999,54.120004],[-132.04948,52.984621],[-131.179043,52.180433],[-131.57783,52.182371],[-132.180428,52.639707],[-132.549992,53.100015],[-133.054611,53.411469],[-133.239664,53.85108],[-133.180004,54.169975],[-132.710008,54.040009]]],[[[-79.26582,62.158675],[-79.65752,61.63308],[-80.09956,61.7181],[-80.36215,62.01649],[-80.315395,62.085565],[-79.92939,62.3856],[-79.52002,62.36371],[-79.26582,62.158675]]],[[[-81.89825,62.7108],[-83.06857,62.15922],[-83.77462,62.18231],[-83.99367,62.4528],[-83.25048,62.91409],[-81.87699,62.90458],[-81.89825,62.7108]]],[[[-85.161308,65.657285],[-84.975764,65.217518],[-84.464012,65.371772],[-83.882626,65.109618],[-82.787577,64.766693],[-81.642014,64.455136],[-81.55344,63.979609],[-80.817361,64.057486],[-80.103451,63.725981],[-80.99102,63.411246],[-82.547178,63.651722],[-83.108798,64.101876],[-84.100417,63.569712],[-85.523405,63.052379],[-85.866769,63.637253],[-87.221983,63.541238],[-86.35276,64.035833],[-86.224886,64.822917],[-85.883848,65.738778],[-85.161308,65.657285]]],[[[-75.86588,67.14886],[-76.98687,67.09873],[-77.2364,67.58809],[-76.81166,68.14856],[-75.89521,68.28721],[-75.1145,68.01036],[-75.10333,67.58202],[-75.21597,67.44425],[-75.86588,67.14886]]],[[[-95.647681,69.10769],[-96.269521,68.75704],[-97.617401,69.06003],[-98.431801,68.9507],[-99.797401,69.40003],[-98.917401,69.71003],[-98.218261,70.14354],[-97.157401,69.86003],[-96.557401,69.68003],[-96.257401,69.49003],[-95.647681,69.10769]]],[[[-90.5471,69.49766],[-90.55151,68.47499],[-89.21515,69.25873],[-88.01966,68.61508],[-88.31749,67.87338],[-87.35017,67.19872],[-86.30607,67.92146],[-85.57664,68.78456],[-85.52197,69.88211],[-84.10081,69.80539],[-82.62258,69.65826],[-81.28043,69.16202],[-81.2202,68.66567],[-81.96436,68.13253],[-81.25928,67.59716],[-81.38653,67.11078],[-83.34456,66.41154],[-84.73542,66.2573],[-85.76943,66.55833],[-86.0676,66.05625],[-87.03143,65.21297],[-87.32324,64.77563],[-88.48296,64.09897],[-89.91444,64.03273],[-90.70398,63.61017],[-90.77004,62.96021],[-91.93342,62.83508],[-93.15698,62.02469],[-94.24153,60.89865],[-94.62931,60.11021],[-94.6846,58.94882],[-93.21502,58.78212],[-92.76462,57.84571],[-92.29703,57.08709],[-90.89769,57.28468],[-89.03953,56.85172],[-88.03978,56.47162],[-87.32421,55.99914],[-86.07121,55.72383],[-85.01181,55.3026],[-83.36055,55.24489],[-82.27285,55.14832],[-82.4362,54.28227],[-82.12502,53.27703],[-81.40075,52.15788],[-79.91289,51.20842],[-79.14301,51.53393],[-78.60191,52.56208],[-79.12421,54.14145],[-79.82958,54.66772],[-78.22874,55.13645],[-77.0956,55.83741],[-76.54137,56.53423],[-76.62319,57.20263],[-77.30226,58.05209],[-78.51688,58.80458],[-77.33676,59.85261],[-77.77272,60.75788],[-78.10687,62.31964],[-77.41067,62.55053],[-75.69621,62.2784],[-74.6682,62.18111],[-73.83988,62.4438],[-72.90853,62.10507],[-71.67708,61.52535],[-71.37369,61.13717],[-69.59042,61.06141],[-69.62033,60.22125],[-69.2879,58.95736],[-68.37455,58.80106],[-67.64976,58.21206],[-66.20178,58.76731],[-65.24517,59.87071],[-64.58352,60.33558],[-63.80475,59.4426],[-62.50236,58.16708],[-61.39655,56.96745],[-61.79866,56.33945],[-60.46853,55.77548],[-59.56962,55.20407],[-57.97508,54.94549],[-57.3332,54.6265],[-56.93689,53.78032],[-56.15811,53.64749],[-55.75632,53.27036],[-55.68338,52.14664],[-56.40916,51.7707],[-57.12691,51.41972],[-58.77482,51.0643],[-60.03309,50.24277],[-61.72366,50.08046],[-63.86251,50.29099],[-65.36331,50.2982],[-66.39905,50.22897],[-67.23631,49.51156],[-68.51114,49.06836],[-69.95362,47.74488],[-71.10458,46.82171],[-70.25522,46.98606],[-68.65,48.3],[-66.55243,49.1331],[-65.05626,49.23278],[-64.17099,48.74248],[-65.11545,48.07085],[-64.79854,46.99297],[-64.47219,46.23849],[-63.17329,45.73902],[-61.52072,45.88377],[-60.51815,47.00793],[-60.4486,46.28264],[-59.80287,45.9204],[-61.03988,45.26525],[-63.25471,44.67014],[-64.24656,44.26553],[-65.36406,43.54523],[-66.1234,43.61867],[-66.16173,44.46512],[-64.42549,45.29204],[-66.02605,45.25931],[-67.13741,45.13753],[-67.79134,45.70281],[-67.79046,47.06636],[-68.23444,47.35486],[-68.905,47.185],[-69.237216,47.447781],[-69.99997,46.69307],[-70.305,45.915],[-70.66,45.46],[-71.08482,45.30524],[-71.405,45.255],[-71.50506,45.0082],[-73.34783,45.00738],[-74.867,45.00048],[-75.31821,44.81645],[-76.375,44.09631],[-76.5,44.018459],[-76.820034,43.628784],[-77.737885,43.629056],[-78.72028,43.625089],[-79.171674,43.466339],[-79.01,43.27],[-78.92,42.965],[-78.939362,42.863611],[-80.247448,42.3662],[-81.277747,42.209026],[-82.439278,41.675105],[-82.690089,41.675105],[-83.02981,41.832796],[-83.142,41.975681],[-83.12,42.08],[-82.9,42.43],[-82.43,42.98],[-82.137642,43.571088],[-82.337763,44.44],[-82.550925,45.347517],[-83.592851,45.816894],[-83.469551,45.994686],[-83.616131,46.116927],[-83.890765,46.116927],[-84.091851,46.275419],[-84.14212,46.512226],[-84.3367,46.40877],[-84.6049,46.4396],[-84.543749,46.538684],[-84.779238,46.637102],[-84.87608,46.900083],[-85.652363,47.220219],[-86.461991,47.553338],[-87.439793,47.94],[-88.378114,48.302918],[-89.272917,48.019808],[-89.6,48.01],[-90.83,48.27],[-91.64,48.14],[-92.61,48.45],[-93.63087,48.60926],[-94.32914,48.67074],[-94.64,48.84],[-94.81758,49.38905],[-95.15609,49.38425],[-95.15907,49],[-97.22872,49.0007],[-100.65,49],[-104.04826,48.99986],[-107.05,49],[-110.05,49],[-113,49],[-116.04818,49],[-117.03121,49],[-120,49],[-122.84,49],[-122.97421,49.002538],[-124.91024,49.98456],[-125.62461,50.41656],[-127.43561,50.83061],[-127.99276,51.71583],[-127.85032,52.32961],[-129.12979,52.75538],[-129.30523,53.56159],[-130.51497,54.28757],[-130.53611,54.80278],[-129.98,55.285],[-130.00778,55.91583],[-131.70781,56.55212],[-132.73042,57.69289],[-133.35556,58.41028],[-134.27111,58.86111],[-134.945,59.27056],[-135.47583,59.78778],[-136.47972,59.46389],[-137.4525,58.905],[-138.34089,59.56211],[-139.039,60],[-140.013,60.27682],[-140.99778,60.30639],[-140.9925,66.00003],[-140.986,69.712],[-139.12052,69.47102],[-137.54636,68.99002],[-136.50358,68.89804],[-135.62576,69.31512],[-134.41464,69.62743],[-132.92925,69.50534],[-131.43136,69.94451],[-129.79471,70.19369],[-129.10773,69.77927],[-128.36156,70.01286],[-128.13817,70.48384],[-127.44712,70.37721],[-125.75632,69.48058],[-124.42483,70.1584],[-124.28968,69.39969],[-123.06108,69.56372],[-122.6835,69.85553],[-121.47226,69.79778],[-119.94288,69.37786],[-117.60268,69.01128],[-116.22643,68.84151],[-115.2469,68.90591],[-113.89794,68.3989],[-115.30489,67.90261],[-113.49727,67.68815],[-110.798,67.80612],[-109.94619,67.98104],[-108.8802,67.38144],[-107.79239,67.88736],[-108.81299,68.31164],[-108.16721,68.65392],[-106.95,68.7],[-106.15,68.8],[-105.34282,68.56122],[-104.33791,68.018],[-103.22115,68.09775],[-101.45433,67.64689],[-99.90195,67.80566],[-98.4432,67.78165],[-98.5586,68.40394],[-97.66948,68.57864],[-96.11991,68.23939],[-96.12588,67.29338],[-95.48943,68.0907],[-94.685,68.06383],[-94.23282,69.06903],[-95.30408,69.68571],[-96.47131,70.08976],[-96.39115,71.19482],[-95.2088,71.92053],[-93.88997,71.76015],[-92.87818,71.31869],[-91.51964,70.19129],[-92.40692,69.69997],[-90.5471,69.49766]]],[[[-114.16717,73.12145],[-114.66634,72.65277],[-112.44102,72.9554],[-111.05039,72.4504],[-109.92035,72.96113],[-109.00654,72.63335],[-108.18835,71.65089],[-107.68599,72.06548],[-108.39639,73.08953],[-107.51645,73.23598],[-106.52259,73.07601],[-105.40246,72.67259],[-104.77484,71.6984],[-104.46476,70.99297],[-102.78537,70.49776],[-100.98078,70.02432],[-101.08929,69.58447],[-102.73116,69.50402],[-102.09329,69.11962],[-102.43024,68.75282],[-104.24,68.91],[-105.96,69.18],[-107.12254,69.11922],[-109,68.78],[-111.534149,68.630059],[-113.3132,68.53554],[-113.85496,69.00744],[-115.22,69.28],[-116.10794,69.16821],[-117.34,69.96],[-116.67473,70.06655],[-115.13112,70.2373],[-113.72141,70.19237],[-112.4161,70.36638],[-114.35,70.6],[-116.48684,70.52045],[-117.9048,70.54056],[-118.43238,70.9092],[-116.11311,71.30918],[-117.65568,71.2952],[-119.40199,71.55859],[-118.56267,72.30785],[-117.86642,72.70594],[-115.18909,73.31459],[-114.16717,73.12145]]],[[[-104.5,73.42],[-105.38,72.76],[-106.94,73.46],[-106.6,73.6],[-105.26,73.64],[-104.5,73.42]]],[[[-76.34,73.102685],[-76.251404,72.826385],[-77.314438,72.855545],[-78.39167,72.876656],[-79.486252,72.742203],[-79.775833,72.802902],[-80.876099,73.333183],[-80.833885,73.693184],[-80.353058,73.75972],[-78.064438,73.651932],[-76.34,73.102685]]],[[[-86.562179,73.157447],[-85.774371,72.534126],[-84.850112,73.340278],[-82.31559,73.750951],[-80.600088,72.716544],[-80.748942,72.061907],[-78.770639,72.352173],[-77.824624,72.749617],[-75.605845,72.243678],[-74.228616,71.767144],[-74.099141,71.33084],[-72.242226,71.556925],[-71.200015,70.920013],[-68.786054,70.525024],[-67.91497,70.121948],[-66.969033,69.186087],[-68.805123,68.720198],[-66.449866,68.067163],[-64.862314,67.847539],[-63.424934,66.928473],[-61.851981,66.862121],[-62.163177,66.160251],[-63.918444,64.998669],[-65.14886,65.426033],[-66.721219,66.388041],[-68.015016,66.262726],[-68.141287,65.689789],[-67.089646,65.108455],[-65.73208,64.648406],[-65.320168,64.382737],[-64.669406,63.392927],[-65.013804,62.674185],[-66.275045,62.945099],[-68.783186,63.74567],[-67.369681,62.883966],[-66.328297,62.280075],[-66.165568,61.930897],[-68.877367,62.330149],[-71.023437,62.910708],[-72.235379,63.397836],[-71.886278,63.679989],[-73.378306,64.193963],[-74.834419,64.679076],[-74.818503,64.389093],[-77.70998,64.229542],[-78.555949,64.572906],[-77.897281,65.309192],[-76.018274,65.326969],[-73.959795,65.454765],[-74.293883,65.811771],[-73.944912,66.310578],[-72.651167,67.284576],[-72.92606,67.726926],[-73.311618,68.069437],[-74.843307,68.554627],[-76.869101,68.894736],[-76.228649,69.147769],[-77.28737,69.76954],[-78.168634,69.826488],[-78.957242,70.16688],[-79.492455,69.871808],[-81.305471,69.743185],[-84.944706,69.966634],[-87.060003,70.260001],[-88.681713,70.410741],[-89.51342,70.762038],[-88.467721,71.218186],[-89.888151,71.222552],[-90.20516,72.235074],[-89.436577,73.129464],[-88.408242,73.537889],[-85.826151,73.803816],[-86.562179,73.157447]]],[[[-100.35642,73.84389],[-99.16387,73.63339],[-97.38,73.76],[-97.12,73.47],[-98.05359,72.99052],[-96.54,72.56],[-96.72,71.66],[-98.35966,71.27285],[-99.32286,71.35639],[-100.01482,71.73827],[-102.5,72.51],[-102.48,72.83],[-100.43836,72.70588],[-101.54,73.36],[-100.35642,73.84389]]],[[[-93.196296,72.771992],[-94.269047,72.024596],[-95.409856,72.061881],[-96.033745,72.940277],[-96.018268,73.43743],[-95.495793,73.862417],[-94.503658,74.134907],[-92.420012,74.100025],[-90.509793,73.856732],[-92.003965,72.966244],[-93.196296,72.771992]]],[[[-120.46,71.383602],[-123.09219,70.90164],[-123.62,71.34],[-125.928949,71.868688],[-125.5,72.292261],[-124.80729,73.02256],[-123.94,73.68],[-124.91775,74.29275],[-121.53788,74.44893],[-120.10978,74.24135],[-117.55564,74.18577],[-116.58442,73.89607],[-115.51081,73.47519],[-116.76794,73.22292],[-119.22,72.52],[-120.46,71.82],[-120.46,71.383602]]],[[[-93.612756,74.979997],[-94.156909,74.592347],[-95.608681,74.666864],[-96.820932,74.927623],[-96.288587,75.377828],[-94.85082,75.647218],[-93.977747,75.29649],[-93.612756,74.979997]]],[[[-98.5,76.72],[-97.735585,76.25656],[-97.704415,75.74344],[-98.16,75],[-99.80874,74.89744],[-100.88366,75.05736],[-100.86292,75.64075],[-102.50209,75.5638],[-102.56552,76.3366],[-101.48973,76.30537],[-99.98349,76.64634],[-98.57699,76.58859],[-98.5,76.72]]],[[[-108.21141,76.20168],[-107.81943,75.84552],[-106.92893,76.01282],[-105.881,75.9694],[-105.70498,75.47951],[-106.31347,75.00527],[-109.7,74.85],[-112.22307,74.41696],[-113.74381,74.39427],[-113.87135,74.72029],[-111.79421,75.1625],[-116.31221,75.04343],[-117.7104,75.2222],[-116.34602,76.19903],[-115.40487,76.47887],[-112.59056,76.14134],[-110.81422,75.54919],[-109.0671,75.47321],[-110.49726,76.42982],[-109.5811,76.79417],[-108.54859,76.67832],[-108.21141,76.20168]]],[[[-94.684086,77.097878],[-93.573921,76.776296],[-91.605023,76.778518],[-90.741846,76.449597],[-90.969661,76.074013],[-89.822238,75.847774],[-89.187083,75.610166],[-87.838276,75.566189],[-86.379192,75.482421],[-84.789625,75.699204],[-82.753445,75.784315],[-81.128531,75.713983],[-80.057511,75.336849],[-79.833933,74.923127],[-80.457771,74.657304],[-81.948843,74.442459],[-83.228894,74.564028],[-86.097452,74.410032],[-88.15035,74.392307],[-89.764722,74.515555],[-92.422441,74.837758],[-92.768285,75.38682],[-92.889906,75.882655],[-93.893824,76.319244],[-95.962457,76.441381],[-97.121379,76.751078],[-96.745123,77.161389],[-94.684086,77.097878]]],[[[-116.198587,77.645287],[-116.335813,76.876962],[-117.106051,76.530032],[-118.040412,76.481172],[-119.899318,76.053213],[-121.499995,75.900019],[-122.854924,76.116543],[-122.854925,76.116543],[-121.157535,76.864508],[-119.103939,77.51222],[-117.570131,77.498319],[-116.198587,77.645287]]],[[[-93.840003,77.519997],[-94.295608,77.491343],[-96.169654,77.555111],[-96.436304,77.834629],[-94.422577,77.820005],[-93.720656,77.634331],[-93.840003,77.519997]]],[[[-110.186938,77.697015],[-112.051191,77.409229],[-113.534279,77.732207],[-112.724587,78.05105],[-111.264443,78.152956],[-109.854452,77.996325],[-110.186938,77.697015]]],[[[-109.663146,78.601973],[-110.881314,78.40692],[-112.542091,78.407902],[-112.525891,78.550555],[-111.50001,78.849994],[-110.963661,78.804441],[-109.663146,78.601973]]],[[[-95.830295,78.056941],[-97.309843,77.850597],[-98.124289,78.082857],[-98.552868,78.458105],[-98.631984,78.87193],[-97.337231,78.831984],[-96.754399,78.765813],[-95.559278,78.418315],[-95.830295,78.056941]]],[[[-100.060192,78.324754],[-99.670939,77.907545],[-101.30394,78.018985],[-102.949809,78.343229],[-105.176133,78.380332],[-104.210429,78.67742],[-105.41958,78.918336],[-105.492289,79.301594],[-103.529282,79.165349],[-100.825158,78.800462],[-100.060192,78.324754]]],[[[-87.02,79.66],[-85.81435,79.3369],[-87.18756,79.0393],[-89.03535,78.28723],[-90.80436,78.21533],[-92.87669,78.34333],[-93.95116,78.75099],[-93.93574,79.11373],[-93.14524,79.3801],[-94.974,79.37248],[-96.07614,79.70502],[-96.70972,80.15777],[-96.01644,80.60233],[-95.32345,80.90729],[-94.29843,80.97727],[-94.73542,81.20646],[-92.40984,81.25739],[-91.13289,80.72345],[-89.45,80.509322],[-87.81,80.32],[-87.02,79.66]]],[[[-68.5,83.106322],[-65.82735,83.02801],[-63.68,82.9],[-61.85,82.6286],[-61.89388,82.36165],[-64.334,81.92775],[-66.75342,81.72527],[-67.65755,81.50141],[-65.48031,81.50657],[-67.84,80.9],[-69.4697,80.61683],[-71.18,79.8],[-73.2428,79.63415],[-73.88,79.430162],[-76.90773,79.32309],[-75.52924,79.19766],[-76.22046,79.01907],[-75.39345,78.52581],[-76.34354,78.18296],[-77.88851,77.89991],[-78.36269,77.50859],[-79.75951,77.20968],[-79.61965,76.98336],[-77.91089,77.022045],[-77.88911,76.777955],[-80.56125,76.17812],[-83.17439,76.45403],[-86.11184,76.29901],[-87.6,76.42],[-89.49068,76.47239],[-89.6161,76.95213],[-87.76739,77.17833],[-88.26,77.9],[-87.65,77.970222],[-84.97634,77.53873],[-86.34,78.18],[-87.96192,78.37181],[-87.15198,78.75867],[-85.37868,78.9969],[-85.09495,79.34543],[-86.50734,79.73624],[-86.93179,80.25145],[-84.19844,80.20836],[-83.408696,80.1],[-81.84823,80.46442],[-84.1,80.58],[-87.59895,80.51627],[-89.36663,80.85569],[-90.2,81.26],[-91.36786,81.5531],[-91.58702,81.89429],[-90.1,82.085],[-88.93227,82.11751],[-86.97024,82.27961],[-85.5,82.652273],[-84.260005,82.6],[-83.18,82.32],[-82.42,82.86],[-81.1,83.02],[-79.30664,83.13056],[-76.25,83.172059],[-75.71878,83.06404],[-72.83153,83.23324],[-70.665765,83.169781],[-68.5,83.106322]]]]},"type":"Feature","id":"CAN","properties":{"name":"Canada","id":"CAN","centroid":[-75.42,45.27]}},{"geometry":{"type":"Polygon","coordinates":[[[9.594226,47.525058],[9.632932,47.347601],[9.47997,47.10281],[9.932448,46.920728],[10.442701,46.893546],[10.363378,46.483571],[9.922837,46.314899],[9.182882,46.440215],[8.966306,46.036932],[8.489952,46.005151],[8.31663,46.163642],[7.755992,45.82449],[7.273851,45.776948],[6.843593,45.991147],[6.5001,46.429673],[6.022609,46.27299],[6.037389,46.725779],[6.768714,47.287708],[6.736571,47.541801],[7.192202,47.449766],[7.466759,47.620582],[8.317301,47.61358],[8.522612,47.830828],[9.594226,47.525058]]]},"type":"Feature","id":"CHE","properties":{"name":"Switzerland","id":"CHE","centroid":[7.28,46.57]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-68.63401,-52.63637],[-68.63335,-54.8695],[-67.56244,-54.87001],[-66.95992,-54.89681],[-67.29103,-55.30124],[-68.14863,-55.61183],[-68.639991,-55.580018],[-69.2321,-55.49906],[-69.95809,-55.19843],[-71.00568,-55.05383],[-72.2639,-54.49514],[-73.2852,-53.95752],[-74.66253,-52.83749],[-73.8381,-53.04743],[-72.43418,-53.7154],[-71.10773,-54.07433],[-70.59178,-53.61583],[-70.26748,-52.93123],[-69.34565,-52.5183],[-68.63401,-52.63637]]],[[[-68.219913,-21.494347],[-67.82818,-22.872919],[-67.106674,-22.735925],[-66.985234,-22.986349],[-67.328443,-24.025303],[-68.417653,-24.518555],[-68.386001,-26.185016],[-68.5948,-26.506909],[-68.295542,-26.89934],[-69.001235,-27.521214],[-69.65613,-28.459141],[-70.01355,-29.367923],[-69.919008,-30.336339],[-70.535069,-31.36501],[-70.074399,-33.09121],[-69.814777,-33.273886],[-69.817309,-34.193571],[-70.388049,-35.169688],[-70.364769,-36.005089],[-71.121881,-36.658124],[-71.118625,-37.576827],[-70.814664,-38.552995],[-71.413517,-38.916022],[-71.680761,-39.808164],[-71.915734,-40.832339],[-71.746804,-42.051386],[-72.148898,-42.254888],[-71.915424,-43.408565],[-71.464056,-43.787611],[-71.793623,-44.207172],[-71.329801,-44.407522],[-71.222779,-44.784243],[-71.659316,-44.973689],[-71.552009,-45.560733],[-71.917258,-46.884838],[-72.447355,-47.738533],[-72.331161,-48.244238],[-72.648247,-48.878618],[-73.415436,-49.318436],[-73.328051,-50.378785],[-72.975747,-50.74145],[-72.309974,-50.67701],[-72.329404,-51.425956],[-71.914804,-52.009022],[-69.498362,-52.142761],[-68.571545,-52.299444],[-69.461284,-52.291951],[-69.94278,-52.537931],[-70.845102,-52.899201],[-71.006332,-53.833252],[-71.429795,-53.856455],[-72.557943,-53.53141],[-73.702757,-52.835069],[-73.702757,-52.83507],[-74.946763,-52.262754],[-75.260026,-51.629355],[-74.976632,-51.043396],[-75.479754,-50.378372],[-75.608015,-48.673773],[-75.18277,-47.711919],[-74.126581,-46.939253],[-75.644395,-46.647643],[-74.692154,-45.763976],[-74.351709,-44.103044],[-73.240356,-44.454961],[-72.717804,-42.383356],[-73.3889,-42.117532],[-73.701336,-43.365776],[-74.331943,-43.224958],[-74.017957,-41.794813],[-73.677099,-39.942213],[-73.217593,-39.258689],[-73.505559,-38.282883],[-73.588061,-37.156285],[-73.166717,-37.12378],[-72.553137,-35.50884],[-71.861732,-33.909093],[-71.43845,-32.418899],[-71.668721,-30.920645],[-71.370083,-30.095682],[-71.489894,-28.861442],[-70.905124,-27.64038],[-70.724954,-25.705924],[-70.403966,-23.628997],[-70.091246,-21.393319],[-70.16442,-19.756468],[-70.372572,-18.347975],[-69.858444,-18.092694],[-69.590424,-17.580012],[-69.100247,-18.260125],[-68.966818,-18.981683],[-68.442225,-19.405068],[-68.757167,-20.372658],[-68.219913,-21.494347]]]]},"type":"Feature","id":"CHL","properties":{"name":"Chile","id":"CHL","centroid":[-70.4,-33.24]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[121.777818,24.394274],[121.175632,22.790857],[120.74708,21.970571],[120.220083,22.814861],[120.106189,23.556263],[120.69468,24.538451],[121.495044,25.295459],[121.951244,24.997596],[121.777818,24.394274]],[[110.339188,18.678395],[109.47521,18.197701],[108.655208,18.507682],[108.626217,19.367888],[109.119056,19.821039],[110.211599,20.101254],[110.786551,20.077534],[111.010051,19.69593],[110.570647,19.255879],[110.339188,18.678395]]],[[[127.657407,49.76027],[129.397818,49.4406],[130.582293,48.729687],[130.987282,47.790132],[132.506672,47.78897],[133.373596,48.183442],[135.026311,48.47823],[134.500814,47.57844],[134.112362,47.212467],[133.769644,46.116927],[133.097127,45.144066],[131.883454,45.321162],[131.025212,44.967953],[131.288555,44.11152],[131.144688,42.92999],[130.633866,42.903015],[130.640016,42.395009],[129.994267,42.985387],[129.596669,42.424982],[128.052215,41.994285],[128.208433,41.466772],[127.343783,41.503152],[126.869083,41.816569],[126.182045,41.107336],[125.079942,40.569824],[124.265625,39.928493],[122.86757,39.637788],[122.131388,39.170452],[121.054554,38.897471],[121.585995,39.360854],[121.376757,39.750261],[122.168595,40.422443],[121.640359,40.94639],[120.768629,40.593388],[119.639602,39.898056],[119.023464,39.252333],[118.042749,39.204274],[117.532702,38.737636],[118.059699,38.061476],[118.87815,37.897325],[118.911636,37.448464],[119.702802,37.156389],[120.823457,37.870428],[121.711259,37.481123],[122.357937,37.454484],[122.519995,36.930614],[121.104164,36.651329],[120.637009,36.11144],[119.664562,35.609791],[119.151208,34.909859],[120.227525,34.360332],[120.620369,33.376723],[121.229014,32.460319],[121.908146,31.692174],[121.891919,30.949352],[121.264257,30.676267],[121.503519,30.142915],[122.092114,29.83252],[121.938428,29.018022],[121.684439,28.225513],[121.125661,28.135673],[120.395473,27.053207],[119.585497,25.740781],[118.656871,24.547391],[117.281606,23.624501],[115.890735,22.782873],[114.763827,22.668074],[114.152547,22.22376],[113.80678,22.54834],[113.241078,22.051367],[111.843592,21.550494],[110.785466,21.397144],[110.444039,20.341033],[109.889861,20.282457],[109.627655,21.008227],[109.864488,21.395051],[108.522813,21.715212],[108.05018,21.55238],[107.04342,21.811899],[106.567273,22.218205],[106.725403,22.794268],[105.811247,22.976892],[105.329209,23.352063],[104.476858,22.81915],[103.504515,22.703757],[102.706992,22.708795],[102.170436,22.464753],[101.652018,22.318199],[101.80312,21.174367],[101.270026,21.201652],[101.180005,21.436573],[101.150033,21.849984],[100.416538,21.558839],[99.983489,21.742937],[99.240899,22.118314],[99.531992,22.949039],[98.898749,23.142722],[98.660262,24.063286],[97.60472,23.897405],[97.724609,25.083637],[98.671838,25.918703],[98.712094,26.743536],[98.68269,27.508812],[98.246231,27.747221],[97.911988,28.335945],[97.327114,28.261583],[97.402561,27.882536],[97.051989,27.699059],[95.767822,28.139816],[94.306641,27.547242],[93.746338,27.108034],[92.033484,26.83831],[92.103712,27.452614],[91.696657,27.771742],[91.258854,28.040614],[90.730514,28.064954],[90.015829,28.296439],[89.47581,28.042759],[88.814248,27.299316],[88.730326,28.086865],[88.120441,27.876542],[86.954517,27.974262],[85.82332,28.203576],[85.011638,28.642774],[84.23458,28.839894],[83.898993,29.320226],[83.337115,29.463732],[82.327513,30.115268],[81.525804,30.422717],[81.111256,30.183481],[79.721367,30.882715],[78.738894,31.515906],[78.458446,32.618164],[79.176129,32.48378],[79.208892,32.994395],[78.811086,33.506198],[78.912269,34.321936],[77.837451,35.49401],[76.192848,35.898403],[75.896897,36.666806],[75.158028,37.133031],[74.980002,37.41999],[74.829986,37.990007],[74.864816,38.378846],[74.257514,38.606507],[73.928852,38.505815],[73.675379,39.431237],[73.960013,39.660008],[73.822244,39.893973],[74.776862,40.366425],[75.467828,40.562072],[76.526368,40.427946],[76.904484,41.066486],[78.187197,41.185316],[78.543661,41.582243],[80.11943,42.123941],[80.25999,42.349999],[80.18015,42.920068],[80.866206,43.180362],[79.966106,44.917517],[81.947071,45.317027],[82.458926,45.53965],[83.180484,47.330031],[85.16429,47.000956],[85.720484,47.452969],[85.768233,48.455751],[86.598776,48.549182],[87.35997,49.214981],[87.751264,49.297198],[88.013832,48.599463],[88.854298,48.069082],[90.280826,47.693549],[90.970809,46.888146],[90.585768,45.719716],[90.94554,45.286073],[92.133891,45.115076],[93.480734,44.975472],[94.688929,44.352332],[95.306875,44.241331],[95.762455,43.319449],[96.349396,42.725635],[97.451757,42.74889],[99.515817,42.524691],[100.845866,42.663804],[101.83304,42.514873],[103.312278,41.907468],[104.522282,41.908347],[104.964994,41.59741],[106.129316,42.134328],[107.744773,42.481516],[109.243596,42.519446],[110.412103,42.871234],[111.129682,43.406834],[111.829588,43.743118],[111.667737,44.073176],[111.348377,44.457442],[111.873306,45.102079],[112.436062,45.011646],[113.463907,44.808893],[114.460332,45.339817],[115.985096,45.727235],[116.717868,46.388202],[117.421701,46.672733],[118.874326,46.805412],[119.66327,46.69268],[119.772824,47.048059],[118.866574,47.74706],[118.064143,48.06673],[117.295507,47.697709],[116.308953,47.85341],[115.742837,47.726545],[115.485282,48.135383],[116.191802,49.134598],[116.678801,49.888531],[117.879244,49.510983],[119.288461,50.142883],[119.279366,50.582908],[120.18205,51.643566],[120.738191,51.964115],[120.725789,52.516226],[120.177089,52.753886],[121.003085,53.251401],[122.245748,53.431726],[123.571507,53.458804],[125.068211,53.161045],[125.946349,52.792799],[126.564399,51.784255],[126.939157,51.353894],[127.287456,50.739797],[127.657407,49.76027]]],[[[122.50662231445312,23.467723846435547],[122.78874206542969,24.572216033935547],[122.50662231445312,23.467723846435547]]],[[[121.17402648925781,20.826547622680664],[121.91168975830078,21.6975154876709],[121.17402648925781,20.826547622680664]]],[[[120.003662109375,19.03096327846469],[119.47631835937499,18.020527657852337],[120.003662109375,19.03096327846469]]],[[[119.06982421874999,16.024695711685315],[119.05883789062501,15.040295996106765],[119.06982421874999,16.024695711685315]]],[[[118.98193359375,11.953349393643416],[118.531494140625,10.908830155722107],[118.98193359375,11.953349393643416]]],[[[116.23535156249999,7.993957436359008],[115.55419921875,7.18810087117902],[116.23535156249999,7.993957436359008]]],[[[112.8515625,3.743671274749731],[111.785888671875,3.4147247646241174],[112.8515625,3.743671274749731]]],[[[108.30322265624999,5.998533174329341],[108.226318359375,7.089990476360545],[108.30322265624999,5.998533174329341]]],[[[110.06103515625,11.26461221250444],[110.31372070312499,12.264863532756566],[110.06103515625,11.26461221250444]]],[[[109.852294921875,15.178180945596376],[109.302978515625,16.214674588248556],[109.852294921875,15.178180945596376]]],[[[124.54185556547829,25.89184586734392],[124.53009788411983,25.91074214095596],[124.51834020276122,25.930898166142125],[124.54101573109565,25.946015185031744],[124.566804,25.941563],[124.584666,25.908731],[124.56873026572663,25.884707275090506],[124.54185556547829,25.89184586734392]]],[[[123.445178,25.726102],[123.43873310372739,25.753273194189074],[123.46896714150662,25.78308731477693],[123.51347836379274,25.768810130270065],[123.51095886064446,25.71464081258226],[123.46854722431526,25.703722965606424],[123.445178,25.726102]]],[[[119.646064,23.550928],[119.609108,23.503738],[119.578927,23.502641],[119.562297,23.530627],[119.566608,23.584937],[119.601717,23.575613],[119.61034,23.604132],[119.678093,23.600294],[119.691028,23.547087],[119.646064,23.550928]]],[[[123.65247095413902,25.910742140955957],[123.67598631685621,25.947274936605876],[123.70580043744403,25.935517255247277],[123.71503861565435,25.912421809721465],[123.69656225923376,25.8788284344112],[123.66968755898553,25.88680686104739],[123.65247095413902,25.910742140955957]]],[[[119.506246,23.625518],[119.52534,23.62497],[119.519181,23.559705],[119.47237,23.556962],[119.506246,23.577259],[119.506246,23.625518]]],[[[119.497623,23.38679],[119.516717,23.349982],[119.495159,23.349982],[119.497623,23.38679]]],[[[119.557369,23.666634],[119.586318,23.675952],[119.615268,23.661153],[119.608492,23.620035],[119.557369,23.666634]]],[[[122.066706,25.6247],[122.092575,25.639268],[122.087032,25.61067],[122.066706,25.6247]]],[[[121.468013,22.67687],[121.514824,22.676318],[121.513592,22.631582],[121.474788,22.643734],[121.468013,22.67687]]],[[[121.510513,22.086972],[121.575802,22.0842],[121.575186,22.037055],[121.604752,22.022631],[121.594281,21.995443],[121.533918,22.022076],[121.507433,22.048704],[121.510513,22.086972]]],[[[122.097503,25.499987],[122.122141,25.495666],[122.110438,25.465952],[122.097503,25.499987]]],[[[119.421247,23.216949],[119.453275,23.216399],[119.436029,23.186146],[119.421247,23.216949]]],[[[120.355011,22.327439],[120.383344,22.355669],[120.395663,22.342385],[120.355011,22.327439]]]]},"type":"Feature","id":"CHN","properties":{"name":"China","id":"CHN","centroid":[116.2,39.55]}},{"geometry":{"type":"Polygon","coordinates":[[[-2.856125,4.994476],[-3.311084,4.984296],[-4.00882,5.179813],[-4.649917,5.168264],[-5.834496,4.993701],[-6.528769,4.705088],[-7.518941,4.338288],[-7.712159,4.364566],[-7.635368,5.188159],[-7.539715,5.313345],[-7.570153,5.707352],[-7.993693,6.12619],[-8.311348,6.193033],[-8.60288,6.467564],[-8.385452,6.911801],[-8.485446,7.395208],[-8.439298,7.686043],[-8.280703,7.68718],[-8.221792,8.123329],[-8.299049,8.316444],[-8.203499,8.455453],[-7.8321,8.575704],[-8.079114,9.376224],[-8.309616,9.789532],[-8.229337,10.12902],[-8.029944,10.206535],[-7.89959,10.297382],[-7.622759,10.147236],[-6.850507,10.138994],[-6.666461,10.430811],[-6.493965,10.411303],[-6.205223,10.524061],[-6.050452,10.096361],[-5.816926,10.222555],[-5.404342,10.370737],[-4.954653,10.152714],[-4.779884,9.821985],[-4.330247,9.610835],[-3.980449,9.862344],[-3.511899,9.900326],[-2.827496,9.642461],[-2.56219,8.219628],[-2.983585,7.379705],[-3.24437,6.250472],[-2.810701,5.389051],[-2.856125,4.994476]]]},"type":"Feature","id":"CIV","properties":{"name":"Ivory Coast","id":"CIV","centroid":[-5.17,6.49]}},{"geometry":{"type":"Polygon","coordinates":[[[13.075822,2.267097],[12.951334,2.321616],[12.35938,2.192812],[11.751665,2.326758],[11.276449,2.261051],[9.649158,2.283866],[9.795196,3.073404],[9.404367,3.734527],[8.948116,3.904129],[8.744924,4.352215],[8.488816,4.495617],[8.500288,4.771983],[8.757533,5.479666],[9.233163,6.444491],[9.522706,6.453482],[10.118277,7.03877],[10.497375,7.055358],[11.058788,6.644427],[11.745774,6.981383],[11.839309,7.397042],[12.063946,7.799808],[12.218872,8.305824],[12.753672,8.717763],[12.955468,9.417772],[13.1676,9.640626],[13.308676,10.160362],[13.57295,10.798566],[14.415379,11.572369],[14.468192,11.904752],[14.577178,12.085361],[14.181336,12.483657],[14.213531,12.802035],[14.495787,12.859396],[14.893386,12.219048],[14.960152,11.555574],[14.923565,10.891325],[15.467873,9.982337],[14.909354,9.992129],[14.627201,9.920919],[14.171466,10.021378],[13.954218,9.549495],[14.544467,8.965861],[14.979996,8.796104],[15.120866,8.38215],[15.436092,7.692812],[15.27946,7.421925],[14.776545,6.408498],[14.53656,6.226959],[14.459407,5.451761],[14.558936,5.030598],[14.478372,4.732605],[14.950953,4.210389],[15.03622,3.851367],[15.405396,3.335301],[15.862732,3.013537],[15.907381,2.557389],[16.012852,2.26764],[15.940919,1.727673],[15.146342,1.964015],[14.337813,2.227875],[13.075822,2.267097]]]},"type":"Feature","id":"CMR","properties":{"name":"Cameroon","id":"CMR","centroid":[11.35,3.5]}},{"geometry":{"type":"Polygon","coordinates":[[[30.83386,3.509166],[30.773347,2.339883],[31.174149,2.204465],[30.85267,1.849396],[30.468508,1.583805],[30.086154,1.062313],[29.875779,0.59738],[29.819503,-0.20531],[29.587838,-0.587406],[29.579466,-1.341313],[29.291887,-1.620056],[29.254835,-2.21511],[29.117479,-2.292211],[29.024926,-2.839258],[29.276384,-3.293907],[29.339998,-4.499983],[29.519987,-5.419979],[29.419993,-5.939999],[29.620032,-6.520015],[30.199997,-7.079981],[30.740015,-8.340007],[30.346086,-8.238257],[29.002912,-8.407032],[28.734867,-8.526559],[28.449871,-9.164918],[28.673682,-9.605925],[28.49607,-10.789884],[28.372253,-11.793647],[28.642417,-11.971569],[29.341548,-12.360744],[29.616001,-12.178895],[29.699614,-13.257227],[28.934286,-13.248958],[28.523562,-12.698604],[28.155109,-12.272481],[27.388799,-12.132747],[27.16442,-11.608748],[26.553088,-11.92444],[25.75231,-11.784965],[25.418118,-11.330936],[24.78317,-11.238694],[24.314516,-11.262826],[24.257155,-10.951993],[23.912215,-10.926826],[23.456791,-10.867863],[22.837345,-11.017622],[22.402798,-10.993075],[22.155268,-11.084801],[22.208753,-9.894796],[21.875182,-9.523708],[21.801801,-8.908707],[21.949131,-8.305901],[21.746456,-7.920085],[21.728111,-7.290872],[20.514748,-7.299606],[20.601823,-6.939318],[20.091622,-6.94309],[20.037723,-7.116361],[19.417502,-7.155429],[19.166613,-7.738184],[19.016752,-7.988246],[18.464176,-7.847014],[18.134222,-7.987678],[17.47297,-8.068551],[17.089996,-7.545689],[16.860191,-7.222298],[16.57318,-6.622645],[16.326528,-5.87747],[13.375597,-5.864241],[13.024869,-5.984389],[12.735171,-5.965682],[12.322432,-6.100092],[12.182337,-5.789931],[12.436688,-5.684304],[12.468004,-5.248362],[12.631612,-4.991271],[12.995517,-4.781103],[13.25824,-4.882957],[13.600235,-4.500138],[14.144956,-4.510009],[14.209035,-4.793092],[14.582604,-4.970239],[15.170992,-4.343507],[15.75354,-3.855165],[16.00629,-3.535133],[15.972803,-2.712392],[16.407092,-1.740927],[16.865307,-1.225816],[17.523716,-0.74383],[17.638645,-0.424832],[17.663553,-0.058084],[17.82654,0.288923],[17.774192,0.855659],[17.898835,1.741832],[18.094276,2.365722],[18.393792,2.900443],[18.453065,3.504386],[18.542982,4.201785],[18.932312,4.709506],[19.467784,5.031528],[20.290679,4.691678],[20.927591,4.322786],[21.659123,4.224342],[22.405124,4.02916],[22.704124,4.633051],[22.84148,4.710126],[23.297214,4.609693],[24.410531,5.108784],[24.805029,4.897247],[25.128833,4.927245],[25.278798,5.170408],[25.650455,5.256088],[26.402761,5.150875],[27.044065,5.127853],[27.374226,5.233944],[27.979977,4.408413],[28.428994,4.287155],[28.696678,4.455077],[29.159078,4.389267],[29.715995,4.600805],[29.9535,4.173699],[30.83386,3.509166]]]},"type":"Feature","id":"COD","properties":{"name":"Democratic Republic of the Congo","id":"COD","centroid":[15.15,-4.2]}},{"geometry":{"type":"Polygon","coordinates":[[[12.995517,-4.781103],[12.62076,-4.438023],[12.318608,-4.60623],[11.914963,-5.037987],[11.093773,-3.978827],[11.855122,-3.426871],[11.478039,-2.765619],[11.820964,-2.514161],[12.495703,-2.391688],[12.575284,-1.948511],[13.109619,-2.42874],[13.992407,-2.470805],[14.29921,-1.998276],[14.425456,-1.333407],[14.316418,-0.552627],[13.843321,0.038758],[14.276266,1.19693],[14.026669,1.395677],[13.282631,1.314184],[13.003114,1.830896],[13.075822,2.267097],[14.337813,2.227875],[15.146342,1.964015],[15.940919,1.727673],[16.012852,2.26764],[16.537058,3.198255],[17.133042,3.728197],[17.8099,3.560196],[18.453065,3.504386],[18.393792,2.900443],[18.094276,2.365722],[17.898835,1.741832],[17.774192,0.855659],[17.82654,0.288923],[17.663553,-0.058084],[17.638645,-0.424832],[17.523716,-0.74383],[16.865307,-1.225816],[16.407092,-1.740927],[15.972803,-2.712392],[16.00629,-3.535133],[15.75354,-3.855165],[15.170992,-4.343507],[14.582604,-4.970239],[14.209035,-4.793092],[14.144956,-4.510009],[13.600235,-4.500138],[13.25824,-4.882957],[12.995517,-4.781103]]]},"type":"Feature","id":"COG","properties":{"name":"Republic of the Congo","id":"COG","centroid":[15.12,-4.09]}},{"geometry":{"type":"Polygon","coordinates":[[[-75.373223,-0.152032],[-75.801466,0.084801],[-76.292314,0.416047],[-76.57638,0.256936],[-77.424984,0.395687],[-77.668613,0.825893],[-77.855061,0.809925],[-78.855259,1.380924],[-78.990935,1.69137],[-78.617831,1.766404],[-78.662118,2.267355],[-78.42761,2.629556],[-77.931543,2.696606],[-77.510431,3.325017],[-77.12769,3.849636],[-77.496272,4.087606],[-77.307601,4.667984],[-77.533221,5.582812],[-77.318815,5.845354],[-77.476661,6.691116],[-77.881571,7.223771],[-77.753414,7.70984],[-77.431108,7.638061],[-77.242566,7.935278],[-77.474723,8.524286],[-77.353361,8.670505],[-76.836674,8.638749],[-76.086384,9.336821],[-75.6746,9.443248],[-75.664704,9.774003],[-75.480426,10.61899],[-74.906895,11.083045],[-74.276753,11.102036],[-74.197223,11.310473],[-73.414764,11.227015],[-72.627835,11.731972],[-72.238195,11.95555],[-71.75409,12.437303],[-71.399822,12.376041],[-71.137461,12.112982],[-71.331584,11.776284],[-71.973922,11.608672],[-72.227575,11.108702],[-72.614658,10.821975],[-72.905286,10.450344],[-73.027604,9.73677],[-73.304952,9.152],[-72.78873,9.085027],[-72.660495,8.625288],[-72.439862,8.405275],[-72.360901,8.002638],[-72.479679,7.632506],[-72.444487,7.423785],[-72.198352,7.340431],[-71.960176,6.991615],[-70.674234,7.087785],[-70.093313,6.960376],[-69.38948,6.099861],[-68.985319,6.206805],[-68.265052,6.153268],[-67.695087,6.267318],[-67.34144,6.095468],[-67.521532,5.55687],[-67.744697,5.221129],[-67.823012,4.503937],[-67.621836,3.839482],[-67.337564,3.542342],[-67.303173,3.318454],[-67.809938,2.820655],[-67.447092,2.600281],[-67.181294,2.250638],[-66.876326,1.253361],[-67.065048,1.130112],[-67.259998,1.719999],[-67.53781,2.037163],[-67.868565,1.692455],[-69.816973,1.714805],[-69.804597,1.089081],[-69.218638,0.985677],[-69.252434,0.602651],[-69.452396,0.706159],[-70.015566,0.541414],[-70.020656,-0.185156],[-69.577065,-0.549992],[-69.420486,-1.122619],[-69.444102,-1.556287],[-69.893635,-4.298187],[-70.394044,-3.766591],[-70.692682,-3.742872],[-70.047709,-2.725156],[-70.813476,-2.256865],[-71.413646,-2.342802],[-71.774761,-2.16979],[-72.325787,-2.434218],[-73.070392,-2.308954],[-73.659504,-1.260491],[-74.122395,-1.002833],[-74.441601,-0.53082],[-75.106625,-0.057205],[-75.373223,-0.152032]]]},"type":"Feature","id":"COL","properties":{"name":"Colombia","id":"COL","centroid":[-74,4.34]}},{"geometry":{"type":"Polygon","coordinates":[[[-82.965783,8.225028],[-83.508437,8.446927],[-83.711474,8.656836],[-83.596313,8.830443],[-83.632642,9.051386],[-83.909886,9.290803],[-84.303402,9.487354],[-84.647644,9.615537],[-84.713351,9.908052],[-84.97566,10.086723],[-84.911375,9.795992],[-85.110923,9.55704],[-85.339488,9.834542],[-85.660787,9.933347],[-85.797445,10.134886],[-85.791709,10.439337],[-85.659314,10.754331],[-85.941725,10.895278],[-85.71254,11.088445],[-85.561852,11.217119],[-84.903003,10.952303],[-84.673069,11.082657],[-84.355931,10.999226],[-84.190179,10.79345],[-83.895054,10.726839],[-83.655612,10.938764],[-83.40232,10.395438],[-83.015677,9.992982],[-82.546196,9.566135],[-82.932891,9.476812],[-82.927155,9.07433],[-82.719183,8.925709],[-82.868657,8.807266],[-82.829771,8.626295],[-82.913176,8.423517],[-82.965783,8.225028]]]},"type":"Feature","id":"CRI","properties":{"name":"Costa Rica","id":"CRI","centroid":[-84.02,9.55]}},{"geometry":{"type":"Polygon","coordinates":[[[-82.268151,23.188611],[-81.404457,23.117271],[-80.618769,23.10598],[-79.679524,22.765303],[-79.281486,22.399202],[-78.347434,22.512166],[-77.993296,22.277194],[-77.146422,21.657851],[-76.523825,21.20682],[-76.19462,21.220565],[-75.598222,21.016624],[-75.67106,20.735091],[-74.933896,20.693905],[-74.178025,20.284628],[-74.296648,20.050379],[-74.961595,19.923435],[-75.63468,19.873774],[-76.323656,19.952891],[-77.755481,19.855481],[-77.085108,20.413354],[-77.492655,20.673105],[-78.137292,20.739949],[-78.482827,21.028613],[-78.719867,21.598114],[-79.285,21.559175],[-80.217475,21.827324],[-80.517535,22.037079],[-81.820943,22.192057],[-82.169992,22.387109],[-81.795002,22.636965],[-82.775898,22.68815],[-83.494459,22.168518],[-83.9088,22.154565],[-84.052151,21.910575],[-84.54703,21.801228],[-84.974911,21.896028],[-84.447062,22.20495],[-84.230357,22.565755],[-83.77824,22.788118],[-83.267548,22.983042],[-82.510436,23.078747],[-82.268151,23.188611]]]},"type":"Feature","id":"CUB","properties":{"name":"Cuba","id":"CUB","centroid":[-82.21,23.08]}},{"geometry":{"type":"Polygon","coordinates":[[[32.73178,35.140026],[32.802474,35.145504],[32.946961,35.386703],[33.667227,35.373216],[34.576474,35.671596],[33.900804,35.245756],[34.004881,34.978098],[32.979827,34.571869],[32.490296,34.701655],[32.256667,35.103232],[32.73178,35.140026]]]},"type":"Feature","id":"CYP","properties":{"name":"Cyprus","id":"CYP","centroid":[33.25,35.1]}},{"geometry":{"type":"Polygon","coordinates":[[[16.960288,48.596982],[16.499283,48.785808],[16.029647,48.733899],[15.253416,49.039074],[14.901447,48.964402],[14.338898,48.555305],[13.595946,48.877172],[13.031329,49.307068],[12.521024,49.547415],[12.415191,49.969121],[12.240111,50.266338],[12.966837,50.484076],[13.338132,50.733234],[14.056228,50.926918],[14.307013,51.117268],[14.570718,51.002339],[15.016996,51.106674],[15.490972,50.78473],[16.238627,50.697733],[16.176253,50.422607],[16.719476,50.215747],[16.868769,50.473974],[17.554567,50.362146],[17.649445,50.049038],[18.392914,49.988629],[18.853144,49.49623],[18.554971,49.495015],[18.399994,49.315001],[18.170498,49.271515],[18.104973,49.043983],[17.913512,48.996493],[17.886485,48.903475],[17.545007,48.800019],[17.101985,48.816969],[16.960288,48.596982]]]},"type":"Feature","id":"CZE","properties":{"name":"Czech Republic","id":"CZE","centroid":[14.26,50.05]}},{"geometry":{"type":"Polygon","coordinates":[[[9.921906,54.983104],[9.93958,54.596642],[10.950112,54.363607],[10.939467,54.008693],[11.956252,54.196486],[12.51844,54.470371],[13.647467,54.075511],[14.119686,53.757029],[14.353315,53.248171],[14.074521,52.981263],[14.4376,52.62485],[14.685026,52.089947],[14.607098,51.745188],[15.016996,51.106674],[14.570718,51.002339],[14.307013,51.117268],[14.056228,50.926918],[13.338132,50.733234],[12.966837,50.484076],[12.240111,50.266338],[12.415191,49.969121],[12.521024,49.547415],[13.031329,49.307068],[13.595946,48.877172],[13.243357,48.416115],[12.884103,48.289146],[13.025851,47.637584],[12.932627,47.467646],[12.62076,47.672388],[12.141357,47.703083],[11.426414,47.523766],[10.544504,47.566399],[10.402084,47.302488],[9.896068,47.580197],[9.594226,47.525058],[8.522612,47.830828],[8.317301,47.61358],[7.466759,47.620582],[7.593676,48.333019],[8.099279,49.017784],[6.65823,49.201958],[6.18632,49.463803],[6.242751,49.902226],[6.043073,50.128052],[6.156658,50.803721],[5.988658,51.851616],[6.589397,51.852029],[6.84287,52.22844],[7.092053,53.144043],[6.90514,53.482162],[7.100425,53.693932],[7.936239,53.748296],[8.121706,53.527792],[8.800734,54.020786],[8.572118,54.395646],[8.526229,54.962744],[9.282049,54.830865],[9.921906,54.983104]]]},"type":"Feature","id":"DEU","properties":{"name":"Germany","id":"DEU","centroid":[13.25,52.3]}},{"geometry":{"type":"Polygon","coordinates":[[[43.081226,12.699639],[43.317852,12.390148],[43.286381,11.974928],[42.715874,11.735641],[43.145305,11.46204],[42.776852,10.926879],[42.55493,11.10511],[42.31414,11.0342],[41.75557,11.05091],[41.73959,11.35511],[41.66176,11.6312],[42,12.1],[42.35156,12.54223],[42.779642,12.455416],[43.081226,12.699639]]]},"type":"Feature","id":"DJI","properties":{"name":"Djibouti","id":"DJI","centroid":[42.2,11.08]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[12.690006,55.609991],[12.089991,54.800015],[11.043543,55.364864],[10.903914,55.779955],[12.370904,56.111407],[12.690006,55.609991]]],[[[10.912182,56.458621],[10.667804,56.081383],[10.369993,56.190007],[9.649985,55.469999],[9.921906,54.983104],[9.282049,54.830865],[8.526229,54.962744],[8.120311,55.517723],[8.089977,56.540012],[8.256582,56.809969],[8.543438,57.110003],[9.424469,57.172066],[9.775559,57.447941],[10.580006,57.730017],[10.546106,57.215733],[10.25,56.890016],[10.369993,56.609982],[10.912182,56.458621]]]]},"type":"Feature","id":"DNK","properties":{"name":"Denmark","id":"DNK","centroid":[12.34,55.41]}},{"geometry":{"type":"Polygon","coordinates":[[[-71.712361,19.714456],[-71.587304,19.884911],[-70.806706,19.880286],[-70.214365,19.622885],[-69.950815,19.648],[-69.76925,19.293267],[-69.222126,19.313214],[-69.254346,19.015196],[-68.809412,18.979074],[-68.317943,18.612198],[-68.689316,18.205142],[-69.164946,18.422648],[-69.623988,18.380713],[-69.952934,18.428307],[-70.133233,18.245915],[-70.517137,18.184291],[-70.669298,18.426886],[-70.99995,18.283329],[-71.40021,17.598564],[-71.657662,17.757573],[-71.708305,18.044997],[-71.687738,18.31666],[-71.945112,18.6169],[-71.701303,18.785417],[-71.624873,19.169838],[-71.712361,19.714456]]]},"type":"Feature","id":"DOM","properties":{"name":"Dominican Republic","id":"DOM","centroid":[-61.24,15.2]}},{"geometry":{"type":"Polygon","coordinates":[[[11.999506,23.471668],[8.572893,21.565661],[5.677566,19.601207],[4.267419,19.155265],[3.158133,19.057364],[3.146661,19.693579],[2.683588,19.85623],[2.060991,20.142233],[1.823228,20.610809],[-1.550055,22.792666],[-4.923337,24.974574],[-8.6844,27.395744],[-8.665124,27.589479],[-8.66559,27.656426],[-8.674116,28.841289],[-7.059228,29.579228],[-6.060632,29.7317],[-5.242129,30.000443],[-4.859646,30.501188],[-3.690441,30.896952],[-3.647498,31.637294],[-3.06898,31.724498],[-2.616605,32.094346],[-1.307899,32.262889],[-1.124551,32.651522],[-1.388049,32.864015],[-1.733455,33.919713],[-1.792986,34.527919],[-2.169914,35.168396],[-1.208603,35.714849],[-0.127454,35.888662],[0.503877,36.301273],[1.466919,36.605647],[3.161699,36.783905],[4.815758,36.865037],[5.32012,36.716519],[6.26182,37.110655],[7.330385,37.118381],[7.737078,36.885708],[8.420964,36.946427],[8.217824,36.433177],[8.376368,35.479876],[8.140981,34.655146],[7.524482,34.097376],[7.612642,33.344115],[8.430473,32.748337],[8.439103,32.506285],[9.055603,32.102692],[9.48214,30.307556],[9.805634,29.424638],[9.859998,28.95999],[9.683885,28.144174],[9.756128,27.688259],[9.629056,27.140953],[9.716286,26.512206],[9.319411,26.094325],[9.910693,25.365455],[9.948261,24.936954],[10.303847,24.379313],[10.771364,24.562532],[11.560669,24.097909],[11.999506,23.471668]]]},"type":"Feature","id":"DZA","properties":{"name":"Algeria","id":"DZA","centroid":[3.08,36.42]}},{"geometry":{"type":"Polygon","coordinates":[[[-80.302561,-3.404856],[-79.770293,-2.657512],[-79.986559,-2.220794],[-80.368784,-2.685159],[-80.967765,-2.246943],[-80.764806,-1.965048],[-80.933659,-1.057455],[-80.58337,-0.906663],[-80.399325,-0.283703],[-80.020898,0.36034],[-80.09061,0.768429],[-79.542762,0.982938],[-78.855259,1.380924],[-77.855061,0.809925],[-77.668613,0.825893],[-77.424984,0.395687],[-76.57638,0.256936],[-76.292314,0.416047],[-75.801466,0.084801],[-75.373223,-0.152032],[-75.233723,-0.911417],[-75.544996,-1.56161],[-76.635394,-2.608678],[-77.837905,-3.003021],[-78.450684,-3.873097],[-78.639897,-4.547784],[-79.205289,-4.959129],[-79.624979,-4.454198],[-80.028908,-4.346091],[-80.442242,-4.425724],[-80.469295,-4.059287],[-80.184015,-3.821162],[-80.302561,-3.404856]]]},"type":"Feature","id":"ECU","properties":{"name":"Ecuador","id":"ECU","centroid":[-78.35,-0.15]}},{"geometry":{"type":"Polygon","coordinates":[[[34.9226,29.50133],[34.64174,29.09942],[34.42655,28.34399],[34.15451,27.8233],[33.92136,27.6487],[33.58811,27.97136],[33.13676,28.41765],[32.42323,29.85108],[32.32046,29.76043],[32.73482,28.70523],[33.34876,27.69989],[34.10455,26.14227],[34.47387,25.59856],[34.79507,25.03375],[35.69241,23.92671],[35.49372,23.75237],[35.52598,23.10244],[36.69069,22.20485],[36.86623,22],[32.9,22],[29.02,22],[25,22],[25,25.6825],[25,29.238655],[24.70007,30.04419],[24.95762,30.6616],[24.80287,31.08929],[25.16482,31.56915],[26.49533,31.58568],[27.45762,31.32126],[28.45048,31.02577],[28.91353,30.87005],[29.68342,31.18686],[30.09503,31.4734],[30.97693,31.55586],[31.68796,31.4296],[31.96041,30.9336],[32.19247,31.26034],[32.99392,31.02407],[33.7734,30.96746],[34.26544,31.21936],[34.9226,29.50133]]]},"type":"Feature","id":"EGY","properties":{"name":"Egypt","id":"EGY","centroid":[31.14,30.01]}},{"geometry":{"type":"Polygon","coordinates":[[[42.35156,12.54223],[42.00975,12.86582],[41.59856,13.45209],[41.155194,13.77332],[40.8966,14.11864],[40.026219,14.519579],[39.34061,14.53155],[39.0994,14.74064],[38.51295,14.50547],[37.90607,14.95943],[37.59377,14.2131],[36.42951,14.42211],[36.323189,14.822481],[36.75386,16.291874],[36.85253,16.95655],[37.16747,17.26314],[37.904,17.42754],[38.41009,17.998307],[38.990623,16.840626],[39.26611,15.922723],[39.814294,15.435647],[41.179275,14.49108],[41.734952,13.921037],[42.276831,13.343992],[42.589576,13.000421],[43.081226,12.699639],[42.779642,12.455416],[42.35156,12.54223]]]},"type":"Feature","id":"ERI","properties":{"name":"Eritrea","id":"ERI","centroid":[38.55,15.19]}},{"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.984433,42.592775],[-9.392884,43.026625],[-7.97819,43.748338],[-6.754492,43.567909],[-5.411886,43.57424],[-4.347843,43.403449],[-3.517532,43.455901],[-1.901351,43.422802],[-1.502771,43.034014],[0.338047,42.579546],[0.701591,42.795734],[1.826793,42.343385],[2.985999,42.473015],[3.039484,41.89212],[2.091842,41.226089],[0.810525,41.014732],[0.721331,40.678318],[0.106692,40.123934],[-0.278711,39.309978],[0.111291,38.738514],[-0.467124,38.292366],[-0.683389,37.642354],[-1.438382,37.443064],[-2.146453,36.674144],[-3.415781,36.6589],[-4.368901,36.677839],[-4.995219,36.324708],[-5.37716,35.94685],[-5.866432,36.029817],[-6.236694,36.367677],[-6.520191,36.942913],[-7.453726,37.097788],[-7.537105,37.428904],[-7.166508,37.803894],[-7.029281,38.075764],[-7.374092,38.373059],[-7.098037,39.030073],[-7.498632,39.629571],[-7.066592,39.711892],[-7.026413,40.184524],[-6.86402,40.330872],[-6.851127,41.111083],[-6.389088,41.381815],[-6.668606,41.883387],[-7.251309,41.918346],[-7.422513,41.792075],[-8.013175,41.790886],[-8.263857,42.280469],[-8.671946,42.134689],[-9.034818,41.880571]]]},"type":"Feature","id":"ESP","properties":{"name":"Spain","id":"ESP","centroid":[-3.45,40.25]}},{"geometry":{"type":"Polygon","coordinates":[[[24.312863,57.793424],[24.428928,58.383413],[24.061198,58.257375],[23.42656,58.612753],[23.339795,59.18724],[24.604214,59.465854],[25.864189,59.61109],[26.949136,59.445803],[27.981114,59.475388],[28.131699,59.300825],[27.420166,58.724581],[27.716686,57.791899],[27.288185,57.474528],[26.463532,57.476389],[25.60281,57.847529],[25.164594,57.970157],[24.312863,57.793424]]]},"type":"Feature","id":"EST","properties":{"name":"Estonia","id":"EST","centroid":[24.48,59.22]}},{"geometry":{"type":"Polygon","coordinates":[[[37.90607,14.95943],[38.51295,14.50547],[39.0994,14.74064],[39.34061,14.53155],[40.02625,14.51959],[40.8966,14.11864],[41.1552,13.77333],[41.59856,13.45209],[42.00975,12.86582],[42.35156,12.54223],[42,12.1],[41.66176,11.6312],[41.73959,11.35511],[41.75557,11.05091],[42.31414,11.0342],[42.55493,11.10511],[42.776852,10.926879],[42.55876,10.57258],[42.92812,10.02194],[43.29699,9.54048],[43.67875,9.18358],[46.94834,7.99688],[47.78942,8.003],[44.9636,5.00162],[43.66087,4.95755],[42.76967,4.25259],[42.12861,4.23413],[41.855083,3.918912],[41.1718,3.91909],[40.76848,4.25702],[39.85494,3.83879],[39.559384,3.42206],[38.89251,3.50074],[38.67114,3.61607],[38.43697,3.58851],[38.120915,3.598605],[36.855093,4.447864],[36.159079,4.447864],[35.817448,4.776966],[35.817448,5.338232],[35.298007,5.506],[34.70702,6.59422],[34.25032,6.82607],[34.0751,7.22595],[33.56829,7.71334],[32.95418,7.78497],[33.2948,8.35458],[33.8255,8.37916],[33.97498,8.68456],[33.96162,9.58358],[34.25745,10.63009],[34.73115,10.91017],[34.83163,11.31896],[35.26049,12.08286],[35.86363,12.57828],[36.27022,13.56333],[36.42951,14.42211],[37.59377,14.2131],[37.90607,14.95943]]]},"type":"Feature","id":"ETH","properties":{"name":"Ethiopia","id":"ETH","centroid":[38.42,9.02]}},{"geometry":{"type":"Polygon","coordinates":[[[28.59193,69.064777],[28.445944,68.364613],[29.977426,67.698297],[29.054589,66.944286],[30.21765,65.80598],[29.54443,64.948672],[30.444685,64.204453],[30.035872,63.552814],[31.516092,62.867687],[31.139991,62.357693],[30.211107,61.780028],[28.069998,60.503517],[26.255173,60.423961],[24.496624,60.057316],[22.869695,59.846373],[22.290764,60.391921],[21.322244,60.72017],[21.544866,61.705329],[21.059211,62.607393],[21.536029,63.189735],[22.442744,63.81781],[24.730512,64.902344],[25.398068,65.111427],[25.294043,65.534346],[23.903379,66.006927],[23.56588,66.396051],[23.539473,67.936009],[21.978535,68.616846],[20.645593,69.106247],[21.244936,69.370443],[22.356238,68.841741],[23.66205,68.891247],[24.735679,68.649557],[25.689213,69.092114],[26.179622,69.825299],[27.732292,70.164193],[29.015573,69.766491],[28.59193,69.064777]]]},"type":"Feature","id":"FIN","properties":{"name":"Finland","id":"FIN","centroid":[25.03,60.15]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[178.3736,-17.33992],[178.71806,-17.62846],[178.55271,-18.15059],[177.93266,-18.28799],[177.38146,-18.16432],[177.28504,-17.72465],[177.67087,-17.38114],[178.12557,-17.50481],[178.3736,-17.33992]]],[[[179.364143,-16.801354],[178.725059,-17.012042],[178.596839,-16.63915],[179.096609,-16.433984],[179.413509,-16.379054],[180,-16.067133],[180,-16.555217],[179.364143,-16.801354]]],[[[-179.917369,-16.501783],[-180,-16.555217],[-180,-16.067133],[-179.79332,-16.020882],[-179.917369,-16.501783]]]]},"type":"Feature","id":"FJI","properties":{"name":"Fiji","id":"FJI","centroid":[178.3,-18.06]}},{"geometry":{"type":"Polygon","coordinates":[[[-61.2,-51.85],[-60,-51.25],[-59.15,-51.5],[-58.55,-51.1],[-57.75,-51.55],[-58.05,-51.9],[-59.4,-52.2],[-59.85,-51.85],[-60.7,-52.3],[-61.2,-51.85]]]},"type":"Feature","id":"FLK","properties":{"name":"Falkland Islands","id":"FLK","centroid":null}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[9.560016,42.152492],[9.229752,41.380007],[8.775723,41.583612],[8.544213,42.256517],[8.746009,42.628122],[9.390001,43.009985],[9.560016,42.152492]]],[[[3.588184,50.378992],[4.286023,49.907497],[4.799222,49.985373],[5.674052,49.529484],[5.897759,49.442667],[6.18632,49.463803],[6.65823,49.201958],[8.099279,49.017784],[7.593676,48.333019],[7.466759,47.620582],[7.192202,47.449766],[6.736571,47.541801],[6.768714,47.287708],[6.037389,46.725779],[6.022609,46.27299],[6.5001,46.429673],[6.843593,45.991147],[6.802355,45.70858],[7.096652,45.333099],[6.749955,45.028518],[7.007562,44.254767],[7.549596,44.127901],[7.435185,43.693845],[6.529245,43.128892],[4.556963,43.399651],[3.100411,43.075201],[2.985999,42.473015],[1.826793,42.343385],[0.701591,42.795734],[0.338047,42.579546],[-1.502771,43.034014],[-1.901351,43.422802],[-1.384225,44.02261],[-1.193798,46.014918],[-2.225724,47.064363],[-2.963276,47.570327],[-4.491555,47.954954],[-4.59235,48.68416],[-3.295814,48.901692],[-1.616511,48.644421],[-1.933494,49.776342],[-0.989469,49.347376],[1.338761,50.127173],[1.639001,50.946606],[2.513573,51.148506],[2.658422,50.796848],[3.123252,50.780363],[3.588184,50.378992]]]]},"type":"Feature","id":"FRA","properties":{"name":"France","id":"FRA","centroid":[2.2,48.5]}},{"geometry":{"type":"Polygon","coordinates":[[[11.093773,-3.978827],[10.066135,-2.969483],[9.405245,-2.144313],[8.797996,-1.111301],[8.830087,-0.779074],[9.04842,-0.459351],[9.291351,0.268666],[9.492889,1.01012],[9.830284,1.067894],[11.285079,1.057662],[11.276449,2.261051],[11.751665,2.326758],[12.35938,2.192812],[12.951334,2.321616],[13.075822,2.267097],[13.003114,1.830896],[13.282631,1.314184],[14.026669,1.395677],[14.276266,1.19693],[13.843321,0.038758],[14.316418,-0.552627],[14.425456,-1.333407],[14.29921,-1.998276],[13.992407,-2.470805],[13.109619,-2.42874],[12.575284,-1.948511],[12.495703,-2.391688],[11.820964,-2.514161],[11.478039,-2.765619],[11.855122,-3.426871],[11.093773,-3.978827]]]},"type":"Feature","id":"GAB","properties":{"name":"Gabon","id":"GAB","centroid":[9.26,0.25]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-5.661949,54.554603],[-6.197885,53.867565],[-6.95373,54.073702],[-7.572168,54.059956],[-7.366031,54.595841],[-7.572168,55.131622],[-6.733847,55.17286],[-5.661949,54.554603]]],[[[-3.005005,58.635],[-4.073828,57.553025],[-3.055002,57.690019],[-1.959281,57.6848],[-2.219988,56.870017],[-3.119003,55.973793],[-2.085009,55.909998],[-2.005676,55.804903],[-1.114991,54.624986],[-0.430485,54.464376],[0.184981,53.325014],[0.469977,52.929999],[1.681531,52.73952],[1.559988,52.099998],[1.050562,51.806761],[1.449865,51.289428],[0.550334,50.765739],[-0.787517,50.774989],[-2.489998,50.500019],[-2.956274,50.69688],[-3.617448,50.228356],[-4.542508,50.341837],[-5.245023,49.96],[-5.776567,50.159678],[-4.30999,51.210001],[-3.414851,51.426009],[-3.422719,51.426848],[-4.984367,51.593466],[-5.267296,51.9914],[-4.222347,52.301356],[-4.770013,52.840005],[-4.579999,53.495004],[-3.093831,53.404547],[-3.09208,53.404441],[-2.945009,53.985],[-3.614701,54.600937],[-3.630005,54.615013],[-4.844169,54.790971],[-5.082527,55.061601],[-4.719112,55.508473],[-5.047981,55.783986],[-5.586398,55.311146],[-5.644999,56.275015],[-6.149981,56.78501],[-5.786825,57.818848],[-5.009999,58.630013],[-4.211495,58.550845],[-3.005005,58.635]]]]},"type":"Feature","id":"GBR","properties":{"name":"United Kingdom","id":"GBR","centroid":[-0.05,51.36]}},{"geometry":{"type":"Polygon","coordinates":[[[41.554084,41.535656],[41.703171,41.962943],[41.45347,42.645123],[40.875469,43.013628],[40.321394,43.128634],[39.955009,43.434998],[40.076965,43.553104],[40.922185,43.382159],[42.394395,43.220308],[43.756017,42.740828],[43.9312,42.554974],[44.537623,42.711993],[45.470279,42.502781],[45.77641,42.092444],[46.404951,41.860675],[46.145432,41.722802],[46.637908,41.181673],[46.501637,41.064445],[45.962601,41.123873],[45.217426,41.411452],[44.97248,41.248129],[43.582746,41.092143],[42.619549,41.583173],[41.554084,41.535656]]]},"type":"Feature","id":"GEO","properties":{"name":"Georgia","id":"GEO","centroid":[44.5,41.43]}},{"geometry":{"type":"Polygon","coordinates":[[[1.060122,5.928837],[-0.507638,5.343473],[-1.063625,5.000548],[-1.964707,4.710462],[-2.856125,4.994476],[-2.810701,5.389051],[-3.24437,6.250472],[-2.983585,7.379705],[-2.56219,8.219628],[-2.827496,9.642461],[-2.963896,10.395335],[-2.940409,10.96269],[-1.203358,11.009819],[-0.761576,10.93693],[-0.438702,11.098341],[0.023803,11.018682],[-0.049785,10.706918],[0.36758,10.191213],[0.365901,9.465004],[0.461192,8.677223],[0.712029,8.312465],[0.490957,7.411744],[0.570384,6.914359],[0.836931,6.279979],[1.060122,5.928837]]]},"type":"Feature","id":"GHA","properties":{"name":"Ghana","id":"GHA","centroid":[-0.06,5.35]}},{"geometry":{"type":"Polygon","coordinates":[[[-8.439298,7.686043],[-8.722124,7.711674],[-8.926065,7.309037],[-9.208786,7.313921],[-9.403348,7.526905],[-9.33728,7.928534],[-9.755342,8.541055],[-10.016567,8.428504],[-10.230094,8.406206],[-10.505477,8.348896],[-10.494315,8.715541],[-10.65477,8.977178],[-10.622395,9.26791],[-10.839152,9.688246],[-11.117481,10.045873],[-11.917277,10.046984],[-12.150338,9.858572],[-12.425929,9.835834],[-12.596719,9.620188],[-12.711958,9.342712],[-13.24655,8.903049],[-13.685154,9.494744],[-14.074045,9.886167],[-14.330076,10.01572],[-14.579699,10.214467],[-14.693232,10.656301],[-14.839554,10.876572],[-15.130311,11.040412],[-14.685687,11.527824],[-14.382192,11.509272],[-14.121406,11.677117],[-13.9008,11.678719],[-13.743161,11.811269],[-13.828272,12.142644],[-13.718744,12.247186],[-13.700476,12.586183],[-13.217818,12.575874],[-12.499051,12.33209],[-12.278599,12.35444],[-12.203565,12.465648],[-11.658301,12.386583],[-11.513943,12.442988],[-11.456169,12.076834],[-11.297574,12.077971],[-11.036556,12.211245],[-10.87083,12.177887],[-10.593224,11.923975],[-10.165214,11.844084],[-9.890993,12.060479],[-9.567912,12.194243],[-9.327616,12.334286],[-9.127474,12.30806],[-8.905265,12.088358],[-8.786099,11.812561],[-8.376305,11.393646],[-8.581305,11.136246],[-8.620321,10.810891],[-8.407311,10.909257],[-8.282357,10.792597],[-8.335377,10.494812],[-8.029944,10.206535],[-8.229337,10.12902],[-8.309616,9.789532],[-8.079114,9.376224],[-7.8321,8.575704],[-8.203499,8.455453],[-8.299049,8.316444],[-8.221792,8.123329],[-8.280703,7.68718],[-8.439298,7.686043]]]},"type":"Feature","id":"GIN","properties":{"name":"Guinea","id":"GIN","centroid":[-13.49,9.29]}},{"geometry":{"type":"Polygon","coordinates":[[[-16.841525,13.151394],[-16.713729,13.594959],[-15.624596,13.623587],[-15.39877,13.860369],[-15.081735,13.876492],[-14.687031,13.630357],[-14.376714,13.62568],[-14.046992,13.794068],[-13.844963,13.505042],[-14.277702,13.280585],[-14.712197,13.298207],[-15.141163,13.509512],[-15.511813,13.27857],[-15.691001,13.270353],[-15.931296,13.130284],[-16.841525,13.151394]]]},"type":"Feature","id":"GMB","properties":{"name":"Gambia","id":"GMB","centroid":[-16.4,13.28]}},{"geometry":{"type":"Polygon","coordinates":[[[-15.130311,11.040412],[-15.66418,11.458474],[-16.085214,11.524594],[-16.314787,11.806515],[-16.308947,11.958702],[-16.613838,12.170911],[-16.677452,12.384852],[-16.147717,12.547762],[-15.816574,12.515567],[-15.548477,12.62817],[-13.700476,12.586183],[-13.718744,12.247186],[-13.828272,12.142644],[-13.743161,11.811269],[-13.9008,11.678719],[-14.121406,11.677117],[-14.382192,11.509272],[-14.685687,11.527824],[-15.130311,11.040412]]]},"type":"Feature","id":"GNB","properties":{"name":"Guinea Bissau","id":"GNB","centroid":[-15.45,11.45]}},{"geometry":{"type":"Polygon","coordinates":[[[9.492889,1.01012],[9.305613,1.160911],[9.649158,2.283866],[11.276449,2.261051],[11.285079,1.057662],[9.830284,1.067894],[9.492889,1.01012]]]},"type":"Feature","id":"GNQ","properties":{"name":"Equatorial Guinea","id":"GNQ","centroid":[8.5,3.45]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[23.69998,35.705004],[24.246665,35.368022],[25.025015,35.424996],[25.769208,35.354018],[25.745023,35.179998],[26.290003,35.29999],[26.164998,35.004995],[24.724982,34.919988],[24.735007,35.084991],[23.514978,35.279992],[23.69998,35.705004]]],[[[26.604196,41.562115],[26.294602,40.936261],[26.056942,40.824123],[25.447677,40.852545],[24.925848,40.947062],[23.714811,40.687129],[24.407999,40.124993],[23.899968,39.962006],[23.342999,39.960998],[22.813988,40.476005],[22.626299,40.256561],[22.849748,39.659311],[23.350027,39.190011],[22.973099,38.970903],[23.530016,38.510001],[24.025025,38.219993],[24.040011,37.655015],[23.115003,37.920011],[23.409972,37.409991],[22.774972,37.30501],[23.154225,36.422506],[22.490028,36.41],[21.670026,36.844986],[21.295011,37.644989],[21.120034,38.310323],[20.730032,38.769985],[20.217712,39.340235],[20.150016,39.624998],[20.615,40.110007],[20.674997,40.435],[20.99999,40.580004],[21.02004,40.842727],[21.674161,40.931275],[22.055378,41.149866],[22.597308,41.130487],[22.76177,41.3048],[22.952377,41.337994],[23.692074,41.309081],[24.492645,41.583896],[25.197201,41.234486],[26.106138,41.328899],[26.117042,41.826905],[26.604196,41.562115]]]]},"type":"Feature","id":"GRC","properties":{"name":"Greece","id":"GRC","centroid":[23.46,37.58]}},{"geometry":{"type":"Polygon","coordinates":[[[-46.76379,82.62796],[-43.40644,83.22516],[-39.89753,83.18018],[-38.62214,83.54905],[-35.08787,83.64513],[-27.10046,83.51966],[-20.84539,82.72669],[-22.69182,82.34165],[-26.51753,82.29765],[-31.9,82.2],[-31.39646,82.02154],[-27.85666,82.13178],[-24.84448,81.78697],[-22.90328,82.09317],[-22.07175,81.73449],[-23.16961,81.15271],[-20.62363,81.52462],[-15.76818,81.91245],[-12.77018,81.71885],[-12.20855,81.29154],[-16.28533,80.58004],[-16.85,80.35],[-20.04624,80.17708],[-17.73035,80.12912],[-18.9,79.4],[-19.70499,78.75128],[-19.67353,77.63859],[-18.47285,76.98565],[-20.03503,76.94434],[-21.67944,76.62795],[-19.83407,76.09808],[-19.59896,75.24838],[-20.66818,75.15585],[-19.37281,74.29561],[-21.59422,74.22382],[-20.43454,73.81713],[-20.76234,73.46436],[-22.17221,73.30955],[-23.56593,73.30663],[-22.31311,72.62928],[-22.29954,72.18409],[-24.27834,72.59788],[-24.79296,72.3302],[-23.44296,72.08016],[-22.13281,71.46898],[-21.75356,70.66369],[-23.53603,70.471],[-24.30702,70.85649],[-25.54341,71.43094],[-25.20135,70.75226],[-26.36276,70.22646],[-23.72742,70.18401],[-22.34902,70.12946],[-25.02927,69.2588],[-27.74737,68.47046],[-30.67371,68.12503],[-31.77665,68.12078],[-32.81105,67.73547],[-34.20196,66.67974],[-36.35284,65.9789],[-37.04378,65.93768],[-38.37505,65.69213],[-39.81222,65.45848],[-40.66899,64.83997],[-40.68281,64.13902],[-41.1887,63.48246],[-42.81938,62.68233],[-42.41666,61.90093],[-42.86619,61.07404],[-43.3784,60.09772],[-44.7875,60.03676],[-46.26364,60.85328],[-48.26294,60.85843],[-49.23308,61.40681],[-49.90039,62.38336],[-51.63325,63.62691],[-52.14014,64.27842],[-52.27659,65.1767],[-53.66166,66.09957],[-53.30161,66.8365],[-53.96911,67.18899],[-52.9804,68.35759],[-51.47536,68.72958],[-51.08041,69.14781],[-50.87122,69.9291],[-52.013585,69.574925],[-52.55792,69.42616],[-53.45629,69.283625],[-54.68336,69.61003],[-54.75001,70.28932],[-54.35884,70.821315],[-53.431315,70.835755],[-51.39014,70.56978],[-53.10937,71.20485],[-54.00422,71.54719],[-55,71.406537],[-55.83468,71.65444],[-54.71819,72.58625],[-55.32634,72.95861],[-56.12003,73.64977],[-57.32363,74.71026],[-58.59679,75.09861],[-58.58516,75.51727],[-61.26861,76.10238],[-63.39165,76.1752],[-66.06427,76.13486],[-68.50438,76.06141],[-69.66485,76.37975],[-71.40257,77.00857],[-68.77671,77.32312],[-66.76397,77.37595],[-71.04293,77.63595],[-73.297,78.04419],[-73.15938,78.43271],[-69.37345,78.91388],[-65.7107,79.39436],[-65.3239,79.75814],[-68.02298,80.11721],[-67.15129,80.51582],[-63.68925,81.21396],[-62.23444,81.3211],[-62.65116,81.77042],[-60.28249,82.03363],[-57.20744,82.19074],[-54.13442,82.19962],[-53.04328,81.88833],[-50.39061,82.43883],[-48.00386,82.06481],[-46.59984,81.985945],[-44.523,81.6607],[-46.9007,82.19979],[-46.76379,82.62796]]]},"type":"Feature","id":"GRL","properties":{"name":"Greenland","id":"GRL","centroid":null}},{"geometry":{"type":"Polygon","coordinates":[[[-90.095555,13.735338],[-90.608624,13.909771],[-91.23241,13.927832],[-91.689747,14.126218],[-92.22775,14.538829],[-92.20323,14.830103],[-92.087216,15.064585],[-92.229249,15.251447],[-91.74796,16.066565],[-90.464473,16.069562],[-90.438867,16.41011],[-90.600847,16.470778],[-90.711822,16.687483],[-91.08167,16.918477],[-91.453921,17.252177],[-91.002269,17.254658],[-91.00152,17.817595],[-90.067934,17.819326],[-89.14308,17.808319],[-89.150806,17.015577],[-89.229122,15.886938],[-88.930613,15.887273],[-88.604586,15.70638],[-88.518364,15.855389],[-88.225023,15.727722],[-88.68068,15.346247],[-89.154811,15.066419],[-89.22522,14.874286],[-89.145535,14.678019],[-89.353326,14.424133],[-89.587343,14.362586],[-89.534219,14.244816],[-89.721934,14.134228],[-90.064678,13.88197],[-90.095555,13.735338]]]},"type":"Feature","id":"GTM","properties":{"name":"Guatemala","id":"GTM","centroid":[-90.22,14.4]}},{"geometry":{"type":"Polygon","coordinates":[[[-52.556425,2.504705],[-52.939657,2.124858],[-53.418465,2.053389],[-53.554839,2.334897],[-53.778521,2.376703],[-54.088063,2.105557],[-54.524754,2.311849],[-54.27123,2.738748],[-54.184284,3.194172],[-54.011504,3.62257],[-54.399542,4.212611],[-54.478633,4.896756],[-53.958045,5.756548],[-53.618453,5.646529],[-52.882141,5.409851],[-51.823343,4.565768],[-51.657797,4.156232],[-52.249338,3.241094],[-52.556425,2.504705]]]},"type":"Feature","id":"GUF","properties":{"name":"French Guiana","id":"GUF","centroid":[-52.18,5.05]}},{"geometry":{"type":"Polygon","coordinates":[[[-59.758285,8.367035],[-59.101684,7.999202],[-58.482962,7.347691],[-58.454876,6.832787],[-58.078103,6.809094],[-57.542219,6.321268],[-57.147436,5.97315],[-57.307246,5.073567],[-57.914289,4.812626],[-57.86021,4.576801],[-58.044694,4.060864],[-57.601569,3.334655],[-57.281433,3.333492],[-57.150098,2.768927],[-56.539386,1.899523],[-56.782704,1.863711],[-57.335823,1.948538],[-57.660971,1.682585],[-58.11345,1.507195],[-58.429477,1.463942],[-58.540013,1.268088],[-59.030862,1.317698],[-59.646044,1.786894],[-59.718546,2.24963],[-59.974525,2.755233],[-59.815413,3.606499],[-59.53804,3.958803],[-59.767406,4.423503],[-60.111002,4.574967],[-59.980959,5.014061],[-60.213683,5.244486],[-60.733574,5.200277],[-61.410303,5.959068],[-61.139415,6.234297],[-61.159336,6.696077],[-60.543999,6.856584],[-60.295668,7.043911],[-60.637973,7.415],[-60.550588,7.779603],[-59.758285,8.367035]]]},"type":"Feature","id":"GUY","properties":{"name":"Guyana","id":"GUY","centroid":[-58.12,6.5]}},{"geometry":{"type":"Polygon","coordinates":[[[-87.316654,12.984686],[-87.489409,13.297535],[-87.793111,13.38448],[-87.723503,13.78505],[-87.859515,13.893312],[-88.065343,13.964626],[-88.503998,13.845486],[-88.541231,13.980155],[-88.843073,14.140507],[-89.058512,14.340029],[-89.353326,14.424133],[-89.145535,14.678019],[-89.22522,14.874286],[-89.154811,15.066419],[-88.68068,15.346247],[-88.225023,15.727722],[-88.121153,15.688655],[-87.901813,15.864458],[-87.61568,15.878799],[-87.522921,15.797279],[-87.367762,15.84694],[-86.903191,15.756713],[-86.440946,15.782835],[-86.119234,15.893449],[-86.001954,16.005406],[-85.683317,15.953652],[-85.444004,15.885749],[-85.182444,15.909158],[-84.983722,15.995923],[-84.52698,15.857224],[-84.368256,15.835158],[-84.063055,15.648244],[-83.773977,15.424072],[-83.410381,15.270903],[-83.147219,14.995829],[-83.489989,15.016267],[-83.628585,14.880074],[-83.975721,14.749436],[-84.228342,14.748764],[-84.449336,14.621614],[-84.649582,14.666805],[-84.820037,14.819587],[-84.924501,14.790493],[-85.052787,14.551541],[-85.148751,14.560197],[-85.165365,14.35437],[-85.514413,14.079012],[-85.698665,13.960078],[-85.801295,13.836055],[-86.096264,14.038187],[-86.312142,13.771356],[-86.520708,13.778487],[-86.755087,13.754845],[-86.733822,13.263093],[-86.880557,13.254204],[-87.005769,13.025794],[-87.316654,12.984686]]]},"type":"Feature","id":"HND","properties":{"name":"Honduras","id":"HND","centroid":[-87.14,14.05]}},{"geometry":{"type":"Polygon","coordinates":[[[18.829838,45.908878],[19.072769,45.521511],[19.390476,45.236516],[19.005486,44.860234],[18.553214,45.08159],[17.861783,45.06774],[17.002146,45.233777],[16.534939,45.211608],[16.318157,45.004127],[15.959367,45.233777],[15.750026,44.818712],[16.23966,44.351143],[16.456443,44.04124],[16.916156,43.667722],[17.297373,43.446341],[17.674922,43.028563],[18.56,42.65],[18.450016,42.479991],[17.50997,42.849995],[16.930006,43.209998],[16.015385,43.507215],[15.174454,44.243191],[15.37625,44.317915],[14.920309,44.738484],[14.901602,45.07606],[14.258748,45.233777],[13.952255,44.802124],[13.656976,45.136935],[13.679403,45.484149],[13.71506,45.500324],[14.411968,45.466166],[14.595109,45.634941],[14.935244,45.471695],[15.327675,45.452316],[15.323954,45.731783],[15.67153,45.834154],[15.768733,46.238108],[16.564808,46.503751],[16.882515,46.380632],[17.630066,45.951769],[18.456062,45.759481],[18.829838,45.908878]]]},"type":"Feature","id":"HRV","properties":{"name":"Croatia","id":"HRV","centroid":[15.58,45.5]}},{"geometry":{"type":"Polygon","coordinates":[[[-73.189791,19.915684],[-72.579673,19.871501],[-71.712361,19.714456],[-71.624873,19.169838],[-71.701303,18.785417],[-71.945112,18.6169],[-71.687738,18.31666],[-71.708305,18.044997],[-72.372476,18.214961],[-72.844411,18.145611],[-73.454555,18.217906],[-73.922433,18.030993],[-74.458034,18.34255],[-74.369925,18.664908],[-73.449542,18.526053],[-72.694937,18.445799],[-72.334882,18.668422],[-72.79165,19.101625],[-72.784105,19.483591],[-73.415022,19.639551],[-73.189791,19.915684]]]},"type":"Feature","id":"HTI","properties":{"name":"Haiti","id":"HTI","centroid":[-72.2,18.4]}},{"geometry":{"type":"Polygon","coordinates":[[[16.202298,46.852386],[16.534268,47.496171],[16.340584,47.712902],[16.903754,47.714866],[16.979667,48.123497],[17.488473,47.867466],[17.857133,47.758429],[18.696513,47.880954],[18.777025,48.081768],[19.174365,48.111379],[19.661364,48.266615],[19.769471,48.202691],[20.239054,48.327567],[20.473562,48.56285],[20.801294,48.623854],[21.872236,48.319971],[22.085608,48.422264],[22.64082,48.15024],[22.710531,47.882194],[22.099768,47.672439],[21.626515,46.994238],[21.021952,46.316088],[20.220192,46.127469],[19.596045,46.17173],[18.829838,45.908878],[18.456062,45.759481],[17.630066,45.951769],[16.882515,46.380632],[16.564808,46.503751],[16.370505,46.841327],[16.202298,46.852386]]]},"type":"Feature","id":"HUN","properties":{"name":"Hungary","id":"HUN","centroid":[19.05,47.29]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[120.715609,-10.239581],[120.295014,-10.25865],[118.967808,-9.557969],[119.90031,-9.36134],[120.425756,-9.665921],[120.775502,-9.969675],[120.715609,-10.239581]]],[[[124.43595,-10.140001],[123.579982,-10.359987],[123.459989,-10.239995],[123.550009,-9.900016],[123.980009,-9.290027],[124.968682,-8.89279],[125.07002,-9.089987],[125.08852,-9.393173],[124.43595,-10.140001]]],[[[117.900018,-8.095681],[118.260616,-8.362383],[118.87846,-8.280683],[119.126507,-8.705825],[117.970402,-8.906639],[117.277731,-9.040895],[116.740141,-9.032937],[117.083737,-8.457158],[117.632024,-8.449303],[117.900018,-8.095681]]],[[[122.903537,-8.094234],[122.756983,-8.649808],[121.254491,-8.933666],[119.924391,-8.810418],[119.920929,-8.444859],[120.715092,-8.236965],[121.341669,-8.53674],[122.007365,-8.46062],[122.903537,-8.094234]]],[[[108.623479,-6.777674],[110.539227,-6.877358],[110.759576,-6.465186],[112.614811,-6.946036],[112.978768,-7.594213],[114.478935,-7.776528],[115.705527,-8.370807],[114.564511,-8.751817],[113.464734,-8.348947],[112.559672,-8.376181],[111.522061,-8.302129],[110.58615,-8.122605],[109.427667,-7.740664],[108.693655,-7.6416],[108.277763,-7.766657],[106.454102,-7.3549],[106.280624,-6.9249],[105.365486,-6.851416],[106.051646,-5.895919],[107.265009,-5.954985],[108.072091,-6.345762],[108.486846,-6.421985],[108.623479,-6.777674]]],[[[134.724624,-6.214401],[134.210134,-6.895238],[134.112776,-6.142467],[134.290336,-5.783058],[134.499625,-5.445042],[134.727002,-5.737582],[134.724624,-6.214401]]],[[[127.249215,-3.459065],[126.874923,-3.790983],[126.183802,-3.607376],[125.989034,-3.177273],[127.000651,-3.129318],[127.249215,-3.459065]]],[[[130.471344,-3.093764],[130.834836,-3.858472],[129.990547,-3.446301],[129.155249,-3.362637],[128.590684,-3.428679],[127.898891,-3.393436],[128.135879,-2.84365],[129.370998,-2.802154],[130.471344,-3.093764]]],[[[134.143368,-1.151867],[134.422627,-2.769185],[135.457603,-3.367753],[136.293314,-2.307042],[137.440738,-1.703513],[138.329727,-1.702686],[139.184921,-2.051296],[139.926684,-2.409052],[141.00021,-2.600151],[141.017057,-5.859022],[141.033852,-9.117893],[140.143415,-8.297168],[139.127767,-8.096043],[138.881477,-8.380935],[137.614474,-8.411683],[138.039099,-7.597882],[138.668621,-7.320225],[138.407914,-6.232849],[137.92784,-5.393366],[135.98925,-4.546544],[135.164598,-4.462931],[133.66288,-3.538853],[133.367705,-4.024819],[132.983956,-4.112979],[132.756941,-3.746283],[132.753789,-3.311787],[131.989804,-2.820551],[133.066845,-2.460418],[133.780031,-2.479848],[133.696212,-2.214542],[132.232373,-2.212526],[131.836222,-1.617162],[130.94284,-1.432522],[130.519558,-0.93772],[131.867538,-0.695461],[132.380116,-0.369538],[133.985548,-0.78021],[134.143368,-1.151867]]],[[[125.240501,1.419836],[124.437035,0.427881],[123.685505,0.235593],[122.723083,0.431137],[121.056725,0.381217],[120.183083,0.237247],[120.04087,-0.519658],[120.935905,-1.408906],[121.475821,-0.955962],[123.340565,-0.615673],[123.258399,-1.076213],[122.822715,-0.930951],[122.38853,-1.516858],[121.508274,-1.904483],[122.454572,-3.186058],[122.271896,-3.5295],[123.170963,-4.683693],[123.162333,-5.340604],[122.628515,-5.634591],[122.236394,-5.282933],[122.719569,-4.464172],[121.738234,-4.851331],[121.489463,-4.574553],[121.619171,-4.188478],[120.898182,-3.602105],[120.972389,-2.627643],[120.305453,-2.931604],[120.390047,-4.097579],[120.430717,-5.528241],[119.796543,-5.6734],[119.366906,-5.379878],[119.653606,-4.459417],[119.498835,-3.494412],[119.078344,-3.487022],[118.767769,-2.801999],[119.180974,-2.147104],[119.323394,-1.353147],[119.825999,0.154254],[120.035702,0.566477],[120.885779,1.309223],[121.666817,1.013944],[122.927567,0.875192],[124.077522,0.917102],[125.065989,1.643259],[125.240501,1.419836]]],[[[128.688249,1.132386],[128.635952,0.258486],[128.12017,0.356413],[127.968034,-0.252077],[128.379999,-0.780004],[128.100016,-0.899996],[127.696475,-0.266598],[127.39949,1.011722],[127.600512,1.810691],[127.932378,2.174596],[128.004156,1.628531],[128.594559,1.540811],[128.688249,1.132386]]],[[[117.875627,1.827641],[118.996747,0.902219],[117.811858,0.784242],[117.478339,0.102475],[117.521644,-0.803723],[116.560048,-1.487661],[116.533797,-2.483517],[116.148084,-4.012726],[116.000858,-3.657037],[114.864803,-4.106984],[114.468652,-3.495704],[113.755672,-3.43917],[113.256994,-3.118776],[112.068126,-3.478392],[111.703291,-2.994442],[111.04824,-3.049426],[110.223846,-2.934032],[110.070936,-1.592874],[109.571948,-1.314907],[109.091874,-0.459507],[108.952658,0.415375],[109.069136,1.341934],[109.66326,2.006467],[109.830227,1.338136],[110.514061,0.773131],[111.159138,0.976478],[111.797548,0.904441],[112.380252,1.410121],[112.859809,1.49779],[113.80585,1.217549],[114.621355,1.430688],[115.134037,2.821482],[115.519078,3.169238],[115.865517,4.306559],[117.015214,4.306094],[117.882035,4.137551],[117.313232,3.234428],[118.04833,2.28769],[117.875627,1.827641]]],[[[105.817655,-5.852356],[104.710384,-5.873285],[103.868213,-5.037315],[102.584261,-4.220259],[102.156173,-3.614146],[101.399113,-2.799777],[100.902503,-2.050262],[100.141981,-0.650348],[99.26374,0.183142],[98.970011,1.042882],[98.601351,1.823507],[97.699598,2.453184],[97.176942,3.308791],[96.424017,3.86886],[95.380876,4.970782],[95.293026,5.479821],[95.936863,5.439513],[97.484882,5.246321],[98.369169,4.26837],[99.142559,3.59035],[99.693998,3.174329],[100.641434,2.099381],[101.658012,2.083697],[102.498271,1.3987],[103.07684,0.561361],[103.838396,0.104542],[103.437645,-0.711946],[104.010789,-1.059212],[104.369991,-1.084843],[104.53949,-1.782372],[104.887893,-2.340425],[105.622111,-2.428844],[106.108593,-3.061777],[105.857446,-4.305525],[105.817655,-5.852356]]]]},"type":"Feature","id":"IDN","properties":{"name":"Indonesia","id":"IDN","centroid":[106.49,-6.09]}},{"geometry":{"type":"Polygon","coordinates":[[[77.837451,35.49401],[78.912269,34.321936],[78.811086,33.506198],[79.208892,32.994395],[79.176129,32.48378],[78.458446,32.618164],[78.738894,31.515906],[79.721367,30.882715],[81.111256,30.183481],[80.476721,29.729865],[80.088425,28.79447],[81.057203,28.416095],[81.999987,27.925479],[83.304249,27.364506],[84.675018,27.234901],[85.251779,26.726198],[86.024393,26.630985],[87.227472,26.397898],[88.060238,26.414615],[88.174804,26.810405],[88.043133,27.445819],[88.120441,27.876542],[88.730326,28.086865],[88.814248,27.299316],[88.835643,27.098966],[89.744528,26.719403],[90.373275,26.875724],[91.217513,26.808648],[92.033484,26.83831],[93.746338,27.108034],[94.306641,27.547242],[95.767822,28.139816],[97.051989,27.699059],[97.133999,27.083774],[96.419366,27.264589],[95.124768,26.573572],[95.155153,26.001307],[94.603249,25.162495],[94.552658,24.675238],[94.106742,23.850741],[93.325188,24.078556],[93.286327,23.043658],[93.060294,22.703111],[93.166128,22.27846],[92.672721,22.041239],[92.146035,23.627499],[91.869928,23.624346],[91.706475,22.985264],[91.158963,23.503527],[91.46773,24.072639],[91.915093,24.130414],[92.376202,24.976693],[91.799596,25.147432],[90.872211,25.132601],[89.920693,25.26975],[89.832481,25.965082],[89.355094,26.014407],[88.563049,26.446526],[88.209789,25.768066],[88.931554,25.238692],[88.306373,24.866079],[88.084422,24.501657],[88.69994,24.233715],[88.52977,23.631142],[88.876312,22.879146],[89.031961,22.055708],[88.888766,21.690588],[88.208497,21.703172],[86.975704,21.495562],[87.033169,20.743308],[86.499351,20.151638],[85.060266,19.478579],[83.941006,18.30201],[83.189217,17.671221],[82.192792,17.016636],[82.191242,16.556664],[81.692719,16.310219],[80.791999,15.951972],[80.324896,15.899185],[80.025069,15.136415],[80.233274,13.835771],[80.286294,13.006261],[79.862547,12.056215],[79.857999,10.357275],[79.340512,10.308854],[78.885345,9.546136],[79.18972,9.216544],[78.277941,8.933047],[77.941165,8.252959],[77.539898,7.965535],[76.592979,8.899276],[76.130061,10.29963],[75.746467,11.308251],[75.396101,11.781245],[74.864816,12.741936],[74.616717,13.992583],[74.443859,14.617222],[73.534199,15.990652],[73.119909,17.92857],[72.820909,19.208234],[72.824475,20.419503],[72.630533,21.356009],[71.175273,20.757441],[70.470459,20.877331],[69.16413,22.089298],[69.644928,22.450775],[69.349597,22.84318],[68.176645,23.691965],[68.842599,24.359134],[71.04324,24.356524],[70.844699,25.215102],[70.282873,25.722229],[70.168927,26.491872],[69.514393,26.940966],[70.616496,27.989196],[71.777666,27.91318],[72.823752,28.961592],[73.450638,29.976413],[74.42138,30.979815],[74.405929,31.692639],[75.258642,32.271105],[74.451559,32.7649],[74.104294,33.441473],[73.749948,34.317699],[74.240203,34.748887],[75.757061,34.504923],[76.871722,34.653544],[77.837451,35.49401]]]},"type":"Feature","id":"IND","properties":{"name":"India","id":"IND","centroid":[77.13,28.37]}},{"geometry":{"type":"Polygon","coordinates":[[[-6.197885,53.867565],[-6.032985,53.153164],[-6.788857,52.260118],[-8.561617,51.669301],[-9.977086,51.820455],[-9.166283,52.864629],[-9.688525,53.881363],[-8.327987,54.664519],[-7.572168,55.131622],[-7.366031,54.595841],[-7.572168,54.059956],[-6.95373,54.073702],[-6.197885,53.867565]]]},"type":"Feature","id":"IRL","properties":{"name":"Ireland","id":"IRL","centroid":[-6.15,53.21]}},{"geometry":{"type":"Polygon","coordinates":[[[53.921598,37.198918],[54.800304,37.392421],[55.511578,37.964117],[56.180375,37.935127],[56.619366,38.121394],[57.330434,38.029229],[58.436154,37.522309],[59.234762,37.412988],[60.377638,36.527383],[61.123071,36.491597],[61.210817,35.650072],[60.803193,34.404102],[60.52843,33.676446],[60.9637,33.528832],[60.536078,32.981269],[60.863655,32.18292],[60.941945,31.548075],[61.699314,31.379506],[61.781222,30.73585],[60.874248,29.829239],[61.369309,29.303276],[61.771868,28.699334],[62.72783,28.259645],[62.755426,27.378923],[63.233898,27.217047],[63.316632,26.756532],[61.874187,26.239975],[61.497363,25.078237],[59.616134,25.380157],[58.525761,25.609962],[57.397251,25.739902],[56.970766,26.966106],[56.492139,27.143305],[55.72371,26.964633],[54.71509,26.480658],[53.493097,26.812369],[52.483598,27.580849],[51.520763,27.86569],[50.852948,28.814521],[50.115009,30.147773],[49.57685,29.985715],[48.941333,30.31709],[48.567971,29.926778],[48.014568,30.452457],[48.004698,30.985137],[47.685286,30.984853],[47.849204,31.709176],[47.334661,32.469155],[46.109362,33.017287],[45.416691,33.967798],[45.64846,34.748138],[46.151788,35.093259],[46.07634,35.677383],[45.420618,35.977546],[44.77267,37.17045],[44.225756,37.971584],[44.421403,38.281281],[44.109225,39.428136],[44.79399,39.713003],[44.952688,39.335765],[45.457722,38.874139],[46.143623,38.741201],[46.50572,38.770605],[47.685079,39.508364],[48.060095,39.582235],[48.355529,39.288765],[48.010744,38.794015],[48.634375,38.270378],[48.883249,38.320245],[49.199612,37.582874],[50.147771,37.374567],[50.842354,36.872814],[52.264025,36.700422],[53.82579,36.965031],[53.921598,37.198918]]]},"type":"Feature","id":"IRN","properties":{"name":"Iran","id":"IRN","centroid":[51.25,35.42]}},{"geometry":{"type":"Polygon","coordinates":[[[45.420618,35.977546],[46.07634,35.677383],[46.151788,35.093259],[45.64846,34.748138],[45.416691,33.967798],[46.109362,33.017287],[47.334661,32.469155],[47.849204,31.709176],[47.685286,30.984853],[48.004698,30.985137],[48.014568,30.452457],[48.567971,29.926778],[47.974519,29.975819],[47.302622,30.05907],[46.568713,29.099025],[44.709499,29.178891],[41.889981,31.190009],[40.399994,31.889992],[39.195468,32.161009],[38.792341,33.378686],[41.006159,34.419372],[41.383965,35.628317],[41.289707,36.358815],[41.837064,36.605854],[42.349591,37.229873],[42.779126,37.385264],[43.942259,37.256228],[44.293452,37.001514],[44.772699,37.170445],[45.420618,35.977546]]]},"type":"Feature","id":"IRQ","properties":{"name":"Iraq","id":"IRQ","centroid":[44.3,33.2]}},{"geometry":{"type":"Polygon","coordinates":[[[-14.508695,66.455892],[-14.739637,65.808748],[-13.609732,65.126671],[-14.909834,64.364082],[-17.794438,63.678749],[-18.656246,63.496383],[-19.972755,63.643635],[-22.762972,63.960179],[-21.778484,64.402116],[-23.955044,64.89113],[-22.184403,65.084968],[-22.227423,65.378594],[-24.326184,65.611189],[-23.650515,66.262519],[-22.134922,66.410469],[-20.576284,65.732112],[-19.056842,66.276601],[-17.798624,65.993853],[-16.167819,66.526792],[-14.508695,66.455892]]]},"type":"Feature","id":"ISL","properties":{"name":"Iceland","id":"ISL","centroid":[-21.57,64.1]}},{"geometry":{"type":"Polygon","coordinates":[[[35.719918,32.709192],[35.545665,32.393992],[35.18393,32.532511],[34.974641,31.866582],[35.225892,31.754341],[34.970507,31.616778],[34.927408,31.353435],[35.397561,31.489086],[35.420918,31.100066],[34.922603,29.501326],[34.265433,31.219361],[34.556372,31.548824],[34.488107,31.605539],[34.752587,32.072926],[34.955417,32.827376],[35.098457,33.080539],[35.126053,33.0909],[35.460709,33.08904],[35.552797,33.264275],[35.821101,33.277426],[35.836397,32.868123],[35.700798,32.716014],[35.719918,32.709192]]]},"type":"Feature","id":"ISR","properties":{"name":"Israel","id":"ISR","centroid":[35.12,31.47]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[15.520376,38.231155],[15.160243,37.444046],[15.309898,37.134219],[15.099988,36.619987],[14.335229,36.996631],[13.826733,37.104531],[12.431004,37.61295],[12.570944,38.126381],[13.741156,38.034966],[14.761249,38.143874],[15.520376,38.231155]]],[[[9.210012,41.209991],[9.809975,40.500009],[9.669519,39.177376],[9.214818,39.240473],[8.806936,38.906618],[8.428302,39.171847],[8.388253,40.378311],[8.159998,40.950007],[8.709991,40.899984],[9.210012,41.209991]]],[[[12.376485,46.767559],[13.806475,46.509306],[13.69811,46.016778],[13.93763,45.591016],[13.141606,45.736692],[12.328581,45.381778],[12.383875,44.885374],[12.261453,44.600482],[12.589237,44.091366],[13.526906,43.587727],[14.029821,42.761008],[15.14257,41.95514],[15.926191,41.961315],[16.169897,41.740295],[15.889346,41.541082],[16.785002,41.179606],[17.519169,40.877143],[18.376687,40.355625],[18.480247,40.168866],[18.293385,39.810774],[17.73838,40.277671],[16.869596,40.442235],[16.448743,39.795401],[17.17149,39.4247],[17.052841,38.902871],[16.635088,38.843572],[16.100961,37.985899],[15.684087,37.908849],[15.687963,38.214593],[15.891981,38.750942],[16.109332,38.964547],[15.718814,39.544072],[15.413613,40.048357],[14.998496,40.172949],[14.703268,40.60455],[14.060672,40.786348],[13.627985,41.188287],[12.888082,41.25309],[12.106683,41.704535],[11.191906,42.355425],[10.511948,42.931463],[10.200029,43.920007],[9.702488,44.036279],[8.888946,44.366336],[8.428561,44.231228],[7.850767,43.767148],[7.435185,43.693845],[7.549596,44.127901],[7.007562,44.254767],[6.749955,45.028518],[7.096652,45.333099],[6.802355,45.70858],[6.843593,45.991147],[7.273851,45.776948],[7.755992,45.82449],[8.31663,46.163642],[8.489952,46.005151],[8.966306,46.036932],[9.182882,46.440215],[9.922837,46.314899],[10.363378,46.483571],[10.442701,46.893546],[11.048556,46.751359],[11.164828,46.941579],[12.153088,47.115393],[12.376485,46.767559]]]]},"type":"Feature","id":"ITA","properties":{"name":"Italy","id":"ITA","centroid":[12.29,41.54]}},{"geometry":{"type":"Polygon","coordinates":[[[-77.569601,18.490525],[-76.896619,18.400867],[-76.365359,18.160701],[-76.199659,17.886867],[-76.902561,17.868238],[-77.206341,17.701116],[-77.766023,17.861597],[-78.337719,18.225968],[-78.217727,18.454533],[-77.797365,18.524218],[-77.569601,18.490525]]]},"type":"Feature","id":"JAM","properties":{"name":"Jamaica","id":"JAM","centroid":[-76.5,18]}},{"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.719918,32.709192],[36.834062,32.312938],[38.792341,33.378686],[39.195468,32.161009],[39.004886,32.010217],[37.002166,31.508413],[37.998849,30.5085],[37.66812,30.338665],[37.503582,30.003776],[36.740528,29.865283],[36.501214,29.505254],[36.068941,29.197495],[34.956037,29.356555],[34.922603,29.501326],[35.420918,31.100066],[35.397561,31.489086],[35.545252,31.782505],[35.545665,32.393992]]]},"type":"Feature","id":"JOR","properties":{"name":"Jordan","id":"JOR","centroid":[35.52,31.57]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[134.638428,34.149234],[134.766379,33.806335],[134.203416,33.201178],[133.79295,33.521985],[133.280268,33.28957],[133.014858,32.704567],[132.363115,32.989382],[132.371176,33.463642],[132.924373,34.060299],[133.492968,33.944621],[133.904106,34.364931],[134.638428,34.149234]]],[[[140.976388,37.142074],[140.59977,36.343983],[140.774074,35.842877],[140.253279,35.138114],[138.975528,34.6676],[137.217599,34.606286],[135.792983,33.464805],[135.120983,33.849071],[135.079435,34.596545],[133.340316,34.375938],[132.156771,33.904933],[130.986145,33.885761],[132.000036,33.149992],[131.33279,31.450355],[130.686318,31.029579],[130.20242,31.418238],[130.447676,32.319475],[129.814692,32.61031],[129.408463,33.296056],[130.353935,33.604151],[130.878451,34.232743],[131.884229,34.749714],[132.617673,35.433393],[134.608301,35.731618],[135.677538,35.527134],[136.723831,37.304984],[137.390612,36.827391],[138.857602,37.827485],[139.426405,38.215962],[140.05479,39.438807],[139.883379,40.563312],[140.305783,41.195005],[141.368973,41.37856],[141.914263,39.991616],[141.884601,39.180865],[140.959489,38.174001],[140.976388,37.142074]]],[[[143.910162,44.1741],[144.613427,43.960883],[145.320825,44.384733],[145.543137,43.262088],[144.059662,42.988358],[143.18385,41.995215],[141.611491,42.678791],[141.067286,41.584594],[139.955106,41.569556],[139.817544,42.563759],[140.312087,43.333273],[141.380549,43.388825],[141.671952,44.772125],[141.967645,45.551483],[143.14287,44.510358],[143.910162,44.1741]]]]},"type":"Feature","id":"JPN","properties":{"name":"Japan","id":"JPN","centroid":[139.41,35.41]}},{"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[70.388965,42.081308],[69.070027,41.384244],[68.632483,40.668681],[68.259896,40.662325],[67.985856,41.135991],[66.714047,41.168444],[66.510649,41.987644],[66.023392,41.994646],[66.098012,42.99766],[64.900824,43.728081],[63.185787,43.650075],[62.0133,43.504477],[61.05832,44.405817],[60.239972,44.784037],[58.689989,45.500014],[58.503127,45.586804],[55.928917,44.995858],[55.968191,41.308642],[55.455251,41.259859],[54.755345,42.043971],[54.079418,42.324109],[52.944293,42.116034],[52.50246,41.783316],[52.446339,42.027151],[52.692112,42.443895],[52.501426,42.792298],[51.342427,43.132975],[50.891292,44.031034],[50.339129,44.284016],[50.305643,44.609836],[51.278503,44.514854],[51.316899,45.245998],[52.16739,45.408391],[53.040876,45.259047],[53.220866,46.234646],[53.042737,46.853006],[52.042023,46.804637],[51.191945,47.048705],[50.034083,46.60899],[49.10116,46.39933],[48.593241,46.561034],[48.694734,47.075628],[48.057253,47.743753],[47.315231,47.715847],[46.466446,48.394152],[47.043672,49.152039],[46.751596,49.356006],[47.54948,50.454698],[48.577841,49.87476],[48.702382,50.605128],[50.766648,51.692762],[52.328724,51.718652],[54.532878,51.02624],[55.716941,50.621717],[56.777961,51.043551],[58.363291,51.063653],[59.642282,50.545442],[59.932807,50.842194],[61.337424,50.79907],[61.588003,51.272659],[59.967534,51.96042],[60.927269,52.447548],[60.739993,52.719986],[61.699986,52.979996],[60.978066,53.664993],[61.436591,54.006265],[65.178534,54.354228],[65.666876,54.601267],[68.1691,54.970392],[69.068167,55.38525],[70.865267,55.169734],[71.180131,54.133285],[72.22415,54.376655],[73.508516,54.035617],[73.425679,53.48981],[74.384845,53.546861],[76.8911,54.490524],[76.525179,54.177003],[77.800916,53.404415],[80.03556,50.864751],[80.568447,51.388336],[81.945986,50.812196],[83.383004,51.069183],[83.935115,50.889246],[84.416377,50.3114],[85.11556,50.117303],[85.54127,49.692859],[86.829357,49.826675],[87.35997,49.214981],[86.598776,48.549182],[85.768233,48.455751],[85.720484,47.452969],[85.16429,47.000956],[83.180484,47.330031],[82.458926,45.53965],[81.947071,45.317027],[79.966106,44.917517],[80.866206,43.180362],[80.18015,42.920068],[80.25999,42.349999],[79.643645,42.496683],[79.142177,42.856092],[77.658392,42.960686],[76.000354,42.988022],[75.636965,42.8779],[74.212866,43.298339],[73.645304,43.091272],[73.489758,42.500894],[71.844638,42.845395],[71.186281,42.704293],[70.962315,42.266154]]]},"type":"Feature","id":"KAZ","properties":{"name":"Kazakhstan","id":"KAZ","centroid":[71.3,51.1]}},{"geometry":{"type":"Polygon","coordinates":[[[40.993,-0.85829],[41.58513,-1.68325],[40.88477,-2.08255],[40.63785,-2.49979],[40.26304,-2.57309],[40.12119,-3.27768],[39.80006,-3.68116],[39.60489,-4.34653],[39.20222,-4.67677],[37.7669,-3.67712],[37.69869,-3.09699],[34.07262,-1.05982],[33.903711,-0.95],[33.893569,0.109814],[34.18,0.515],[34.6721,1.17694],[35.03599,1.90584],[34.59607,3.05374],[34.47913,3.5556],[34.005,4.249885],[34.620196,4.847123],[35.298007,5.506],[35.817448,5.338232],[35.817448,4.776966],[36.159079,4.447864],[36.855093,4.447864],[38.120915,3.598605],[38.43697,3.58851],[38.67114,3.61607],[38.89251,3.50074],[39.559384,3.42206],[39.85494,3.83879],[40.76848,4.25702],[41.1718,3.91909],[41.855083,3.918912],[40.98105,2.78452],[40.993,-0.85829]]]},"type":"Feature","id":"KEN","properties":{"name":"Kenya","id":"KEN","centroid":[36.48,-1.17]}},{"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[71.186281,42.704293],[71.844638,42.845395],[73.489758,42.500894],[73.645304,43.091272],[74.212866,43.298339],[75.636965,42.8779],[76.000354,42.988022],[77.658392,42.960686],[79.142177,42.856092],[79.643645,42.496683],[80.25999,42.349999],[80.11943,42.123941],[78.543661,41.582243],[78.187197,41.185316],[76.904484,41.066486],[76.526368,40.427946],[75.467828,40.562072],[74.776862,40.366425],[73.822244,39.893973],[73.960013,39.660008],[73.675379,39.431237],[71.784694,39.279463],[70.549162,39.604198],[69.464887,39.526683],[69.55961,40.103211],[70.648019,39.935754],[71.014198,40.244366],[71.774875,40.145844],[73.055417,40.866033],[71.870115,41.3929],[71.157859,41.143587],[70.420022,41.519998],[71.259248,42.167711],[70.962315,42.266154]]]},"type":"Feature","id":"KGZ","properties":{"name":"Kyrgyzstan","id":"KGZ","centroid":[74.46,42.54]}},{"geometry":{"type":"Polygon","coordinates":[[[103.49728,10.632555],[103.09069,11.153661],[102.584932,12.186595],[102.348099,13.394247],[102.988422,14.225721],[104.281418,14.416743],[105.218777,14.273212],[106.043946,13.881091],[106.496373,14.570584],[107.382727,14.202441],[107.614548,13.535531],[107.491403,12.337206],[105.810524,11.567615],[106.24967,10.961812],[105.199915,10.88931],[104.334335,10.486544],[103.49728,10.632555]]]},"type":"Feature","id":"KHM","properties":{"name":"Cambodia","id":"KHM","centroid":[104.55,11.33]}},{"geometry":{"type":"Polygon","coordinates":[[[128.349716,38.612243],[129.21292,37.432392],[129.46045,36.784189],[129.468304,35.632141],[129.091377,35.082484],[128.18585,34.890377],[127.386519,34.475674],[126.485748,34.390046],[126.37392,34.93456],[126.559231,35.684541],[126.117398,36.725485],[126.860143,36.893924],[126.174759,37.749686],[126.237339,37.840378],[126.68372,37.804773],[127.073309,38.256115],[127.780035,38.304536],[128.205746,38.370397],[128.349716,38.612243]]]},"type":"Feature","id":"KOR","properties":{"name":"South Korea","id":"KOR","centroid":[126.58,37.31]}},{"geometry":{"type":"Polygon","coordinates":[[[47.974519,29.975819],[48.183189,29.534477],[48.093943,29.306299],[48.416094,28.552004],[47.708851,28.526063],[47.459822,29.002519],[46.568713,29.099025],[47.302622,30.05907],[47.974519,29.975819]]]},"type":"Feature","id":"KWT","properties":{"name":"Kuwait","id":"KWT","centroid":[48,29.3]}},{"geometry":{"type":"Polygon","coordinates":[[[105.218777,14.273212],[105.544338,14.723934],[105.589039,15.570316],[104.779321,16.441865],[104.716947,17.428859],[103.956477,18.240954],[103.200192,18.309632],[102.998706,17.961695],[102.413005,17.932782],[102.113592,18.109102],[101.059548,17.512497],[101.035931,18.408928],[101.282015,19.462585],[100.606294,19.508344],[100.548881,20.109238],[100.115988,20.41785],[100.329101,20.786122],[101.180005,21.436573],[101.270026,21.201652],[101.80312,21.174367],[101.652018,22.318199],[102.170436,22.464753],[102.754896,21.675137],[103.203861,20.766562],[104.435,20.758733],[104.822574,19.886642],[104.183388,19.624668],[103.896532,19.265181],[105.094598,18.666975],[105.925762,17.485315],[106.556008,16.604284],[107.312706,15.908538],[107.564525,15.202173],[107.382727,14.202441],[106.496373,14.570584],[106.043946,13.881091],[105.218777,14.273212]]]},"type":"Feature","id":"LAO","properties":{"name":"Laos","id":"LAO","centroid":[102.36,17.58]}},{"geometry":{"type":"Polygon","coordinates":[[[35.821101,33.277426],[35.552797,33.264275],[35.460709,33.08904],[35.126053,33.0909],[35.482207,33.90545],[35.979592,34.610058],[35.998403,34.644914],[36.448194,34.593935],[36.61175,34.201789],[36.06646,33.824912],[35.821101,33.277426]]]},"type":"Feature","id":"LBN","properties":{"name":"Lebanon","id":"LBN","centroid":[35.31,33.53]}},{"geometry":{"type":"Polygon","coordinates":[[[-7.712159,4.364566],[-7.974107,4.355755],[-9.004794,4.832419],[-9.91342,5.593561],[-10.765384,6.140711],[-11.438779,6.785917],[-11.199802,7.105846],[-11.146704,7.396706],[-10.695595,7.939464],[-10.230094,8.406206],[-10.016567,8.428504],[-9.755342,8.541055],[-9.33728,7.928534],[-9.403348,7.526905],[-9.208786,7.313921],[-8.926065,7.309037],[-8.722124,7.711674],[-8.439298,7.686043],[-8.485446,7.395208],[-8.385452,6.911801],[-8.60288,6.467564],[-8.311348,6.193033],[-7.993693,6.12619],[-7.570153,5.707352],[-7.539715,5.313345],[-7.635368,5.188159],[-7.712159,4.364566]]]},"type":"Feature","id":"LBR","properties":{"name":"Liberia","id":"LBR","centroid":[-10.47,6.18]}},{"geometry":{"type":"Polygon","coordinates":[[[14.8513,22.86295],[14.143871,22.491289],[13.581425,23.040506],[11.999506,23.471668],[11.560669,24.097909],[10.771364,24.562532],[10.303847,24.379313],[9.948261,24.936954],[9.910693,25.365455],[9.319411,26.094325],[9.716286,26.512206],[9.629056,27.140953],[9.756128,27.688259],[9.683885,28.144174],[9.859998,28.95999],[9.805634,29.424638],[9.48214,30.307556],[9.970017,30.539325],[10.056575,30.961831],[9.950225,31.37607],[10.636901,31.761421],[10.94479,32.081815],[11.432253,32.368903],[11.488787,33.136996],[12.66331,32.79278],[13.08326,32.87882],[13.91868,32.71196],[15.24563,32.26508],[15.71394,31.37626],[16.61162,31.18218],[18.02109,30.76357],[19.08641,30.26639],[19.57404,30.52582],[20.05335,30.98576],[19.82033,31.75179],[20.13397,32.2382],[20.85452,32.7068],[21.54298,32.8432],[22.89576,32.63858],[23.2368,32.19149],[23.60913,32.18726],[23.9275,32.01667],[24.92114,31.89936],[25.16482,31.56915],[24.80287,31.08929],[24.95762,30.6616],[24.70007,30.04419],[25,29.238655],[25,25.6825],[25,22],[25,20.00304],[23.85,20],[23.83766,19.58047],[19.84926,21.49509],[15.86085,23.40972],[14.8513,22.86295]]]},"type":"Feature","id":"LBY","properties":{"name":"Libya","id":"LBY","centroid":[13.11,32.52]}},{"geometry":{"type":"Polygon","coordinates":[[[81.787959,7.523055],[81.637322,6.481775],[81.21802,6.197141],[80.348357,5.96837],[79.872469,6.763463],[79.695167,8.200843],[80.147801,9.824078],[80.838818,9.268427],[81.304319,8.564206],[81.787959,7.523055]]]},"type":"Feature","id":"LKA","properties":{"name":"Sri Lanka","id":"LKA","centroid":[79.54,6.54]}},{"geometry":{"type":"Polygon","coordinates":[[[28.978263,-28.955597],[29.325166,-29.257387],[29.018415,-29.743766],[28.8484,-30.070051],[28.291069,-30.226217],[28.107205,-30.545732],[27.749397,-30.645106],[26.999262,-29.875954],[27.532511,-29.242711],[28.074338,-28.851469],[28.5417,-28.647502],[28.978263,-28.955597]]]},"type":"Feature","id":"LSO","properties":{"name":"Lesotho","id":"LSO","centroid":[27.3,-29.18]}},{"geometry":{"type":"Polygon","coordinates":[[[22.731099,54.327537],[22.651052,54.582741],[22.757764,54.856574],[22.315724,55.015299],[21.268449,55.190482],[21.0558,56.031076],[22.201157,56.337802],[23.878264,56.273671],[24.860684,56.372528],[25.000934,56.164531],[25.533047,56.100297],[26.494331,55.615107],[26.588279,55.167176],[25.768433,54.846963],[25.536354,54.282423],[24.450684,53.905702],[23.484128,53.912498],[23.243987,54.220567],[22.731099,54.327537]]]},"type":"Feature","id":"LTU","properties":{"name":"Lithuania","id":"LTU","centroid":[25.19,54.38]}},{"geometry":{"type":"Polygon","coordinates":[[[6.043073,50.128052],[6.242751,49.902226],[6.18632,49.463803],[5.897759,49.442667],[5.674052,49.529484],[5.782417,50.090328],[6.043073,50.128052]]]},"type":"Feature","id":"LUX","properties":{"name":"Luxembourg","id":"LUX","centroid":[6.09,49.37]}},{"geometry":{"type":"Polygon","coordinates":[[[21.0558,56.031076],[21.090424,56.783873],[21.581866,57.411871],[22.524341,57.753374],[23.318453,57.006236],[24.12073,57.025693],[24.312863,57.793424],[25.164594,57.970157],[25.60281,57.847529],[26.463532,57.476389],[27.288185,57.474528],[27.770016,57.244258],[27.855282,56.759326],[28.176709,56.16913],[27.10246,55.783314],[26.494331,55.615107],[25.533047,56.100297],[25.000934,56.164531],[24.860684,56.372528],[23.878264,56.273671],[22.201157,56.337802],[21.0558,56.031076]]]},"type":"Feature","id":"LVA","properties":{"name":"Latvia","id":"LVA","centroid":[24.08,56.53]}},{"geometry":{"type":"Polygon","coordinates":[[[-5.193863,35.755182],[-4.591006,35.330712],[-3.640057,35.399855],[-2.604306,35.179093],[-2.169914,35.168396],[-1.792986,34.527919],[-1.733455,33.919713],[-1.388049,32.864015],[-1.124551,32.651522],[-1.307899,32.262889],[-2.616605,32.094346],[-3.06898,31.724498],[-3.647498,31.637294],[-3.690441,30.896952],[-4.859646,30.501188],[-5.242129,30.000443],[-6.060632,29.7317],[-7.059228,29.579228],[-8.674116,28.841289],[-8.66559,27.656426],[-8.817809,27.656426],[-8.817828,27.656426],[-8.794884,27.120696],[-9.413037,27.088476],[-9.735343,26.860945],[-10.189424,26.860945],[-10.551263,26.990808],[-11.392555,26.883424],[-11.71822,26.104092],[-12.030759,26.030866],[-12.500963,24.770116],[-13.89111,23.691009],[-14.221168,22.310163],[-14.630833,21.86094],[-14.750955,21.5006],[-17.002962,21.420734],[-17.020428,21.42231],[-16.973248,21.885745],[-16.589137,22.158234],[-16.261922,22.67934],[-16.326414,23.017768],[-15.982611,23.723358],[-15.426004,24.359134],[-15.089332,24.520261],[-14.824645,25.103533],[-14.800926,25.636265],[-14.43994,26.254418],[-13.773805,26.618892],[-13.139942,27.640148],[-13.121613,27.654148],[-12.618837,28.038186],[-11.688919,28.148644],[-10.900957,28.832142],[-10.399592,29.098586],[-9.564811,29.933574],[-9.814718,31.177736],[-9.434793,32.038096],[-9.300693,32.564679],[-8.657476,33.240245],[-7.654178,33.697065],[-6.912544,34.110476],[-6.244342,35.145865],[-5.929994,35.759988],[-5.193863,35.755182]]]},"type":"Feature","id":"MAR","properties":{"name":"Morocco","id":"MAR","centroid":[-6.5,34.01]}},{"geometry":{"type":"Polygon","coordinates":[[[26.619337,48.220726],[26.857824,48.368211],[27.522537,48.467119],[28.259547,48.155562],[28.670891,48.118149],[29.122698,47.849095],[29.050868,47.510227],[29.415135,47.346645],[29.559674,46.928583],[29.908852,46.674361],[29.83821,46.525326],[30.024659,46.423937],[29.759972,46.349988],[29.170654,46.379262],[29.072107,46.517678],[28.862972,46.437889],[28.933717,46.25883],[28.659987,45.939987],[28.485269,45.596907],[28.233554,45.488283],[28.054443,45.944586],[28.160018,46.371563],[28.12803,46.810476],[27.551166,47.405117],[27.233873,47.826771],[26.924176,48.123264],[26.619337,48.220726]]]},"type":"Feature","id":"MDA","properties":{"name":"Moldova","id":"MDA","centroid":[28.51,47]}},{"geometry":{"type":"Polygon","coordinates":[[[49.543519,-12.469833],[49.808981,-12.895285],[50.056511,-13.555761],[50.217431,-14.758789],[50.476537,-15.226512],[50.377111,-15.706069],[50.200275,-16.000263],[49.860606,-15.414253],[49.672607,-15.710204],[49.863344,-16.451037],[49.774564,-16.875042],[49.498612,-17.106036],[49.435619,-17.953064],[49.041792,-19.118781],[48.548541,-20.496888],[47.930749,-22.391501],[47.547723,-23.781959],[47.095761,-24.94163],[46.282478,-25.178463],[45.409508,-25.601434],[44.833574,-25.346101],[44.03972,-24.988345],[43.763768,-24.460677],[43.697778,-23.574116],[43.345654,-22.776904],[43.254187,-22.057413],[43.433298,-21.336475],[43.893683,-21.163307],[43.89637,-20.830459],[44.374325,-20.072366],[44.464397,-19.435454],[44.232422,-18.961995],[44.042976,-18.331387],[43.963084,-17.409945],[44.312469,-16.850496],[44.446517,-16.216219],[44.944937,-16.179374],[45.502732,-15.974373],[45.872994,-15.793454],[46.312243,-15.780018],[46.882183,-15.210182],[47.70513,-14.594303],[48.005215,-14.091233],[47.869047,-13.663869],[48.293828,-13.784068],[48.84506,-13.089175],[48.863509,-12.487868],[49.194651,-12.040557],[49.543519,-12.469833]]]},"type":"Feature","id":"MDG","properties":{"name":"Madagascar","id":"MDG","centroid":[47.31,-18.55]}},{"geometry":{"type":"Polygon","coordinates":[[[-97.140008,25.869997],[-97.528072,24.992144],[-97.702946,24.272343],[-97.776042,22.93258],[-97.872367,22.444212],[-97.699044,21.898689],[-97.38896,21.411019],[-97.189333,20.635433],[-96.525576,19.890931],[-96.292127,19.320371],[-95.900885,18.828024],[-94.839063,18.562717],[-94.42573,18.144371],[-93.548651,18.423837],[-92.786114,18.524839],[-92.037348,18.704569],[-91.407903,18.876083],[-90.77187,19.28412],[-90.53359,19.867418],[-90.451476,20.707522],[-90.278618,20.999855],[-89.601321,21.261726],[-88.543866,21.493675],[-87.658417,21.458846],[-87.05189,21.543543],[-86.811982,21.331515],[-86.845908,20.849865],[-87.383291,20.255405],[-87.621054,19.646553],[-87.43675,19.472403],[-87.58656,19.04013],[-87.837191,18.259816],[-88.090664,18.516648],[-88.300031,18.499982],[-88.490123,18.486831],[-88.848344,17.883198],[-89.029857,18.001511],[-89.150909,17.955468],[-89.14308,17.808319],[-90.067934,17.819326],[-91.00152,17.817595],[-91.002269,17.254658],[-91.453921,17.252177],[-91.08167,16.918477],[-90.711822,16.687483],[-90.600847,16.470778],[-90.438867,16.41011],[-90.464473,16.069562],[-91.74796,16.066565],[-92.229249,15.251447],[-92.087216,15.064585],[-92.20323,14.830103],[-92.22775,14.538829],[-93.359464,15.61543],[-93.875169,15.940164],[-94.691656,16.200975],[-95.250227,16.128318],[-96.053382,15.752088],[-96.557434,15.653515],[-97.263592,15.917065],[-98.01303,16.107312],[-98.947676,16.566043],[-99.697397,16.706164],[-100.829499,17.171071],[-101.666089,17.649026],[-101.918528,17.91609],[-102.478132,17.975751],[-103.50099,18.292295],[-103.917527,18.748572],[-104.99201,19.316134],[-105.493038,19.946767],[-105.731396,20.434102],[-105.397773,20.531719],[-105.500661,20.816895],[-105.270752,21.076285],[-105.265817,21.422104],[-105.603161,21.871146],[-105.693414,22.26908],[-106.028716,22.773752],[-106.90998,23.767774],[-107.915449,24.548915],[-108.401905,25.172314],[-109.260199,25.580609],[-109.444089,25.824884],[-109.291644,26.442934],[-109.801458,26.676176],[-110.391732,27.162115],[-110.641019,27.859876],[-111.178919,27.941241],[-111.759607,28.467953],[-112.228235,28.954409],[-112.271824,29.266844],[-112.809594,30.021114],[-113.163811,30.786881],[-113.148669,31.170966],[-113.871881,31.567608],[-114.205737,31.524045],[-114.776451,31.799532],[-114.9367,31.393485],[-114.771232,30.913617],[-114.673899,30.162681],[-114.330974,29.750432],[-113.588875,29.061611],[-113.424053,28.826174],[-113.271969,28.754783],[-113.140039,28.411289],[-112.962298,28.42519],[-112.761587,27.780217],[-112.457911,27.525814],[-112.244952,27.171727],[-111.616489,26.662817],[-111.284675,25.73259],[-110.987819,25.294606],[-110.710007,24.826004],[-110.655049,24.298595],[-110.172856,24.265548],[-109.771847,23.811183],[-109.409104,23.364672],[-109.433392,23.185588],[-109.854219,22.818272],[-110.031392,22.823078],[-110.295071,23.430973],[-110.949501,24.000964],[-111.670568,24.484423],[-112.182036,24.738413],[-112.148989,25.470125],[-112.300711,26.012004],[-112.777297,26.32196],[-113.464671,26.768186],[-113.59673,26.63946],[-113.848937,26.900064],[-114.465747,27.14209],[-115.055142,27.722727],[-114.982253,27.7982],[-114.570366,27.741485],[-114.199329,28.115003],[-114.162018,28.566112],[-114.931842,29.279479],[-115.518654,29.556362],[-115.887365,30.180794],[-116.25835,30.836464],[-116.721526,31.635744],[-117.12776,32.53534],[-115.99135,32.61239],[-114.72139,32.72083],[-114.815,32.52528],[-113.30498,32.03914],[-111.02361,31.33472],[-109.035,31.34194],[-108.24194,31.34222],[-108.24,31.754854],[-106.50759,31.75452],[-106.1429,31.39995],[-105.63159,31.08383],[-105.03737,30.64402],[-104.70575,30.12173],[-104.45697,29.57196],[-103.94,29.27],[-103.11,28.97],[-102.48,29.76],[-101.6624,29.7793],[-100.9576,29.38071],[-100.45584,28.69612],[-100.11,28.11],[-99.52,27.54],[-99.3,26.84],[-99.02,26.37],[-98.24,26.06],[-97.53,25.84],[-97.140008,25.869997]]]},"type":"Feature","id":"MEX","properties":{"name":"Mexico","id":"MEX","centroid":[-99.1,19.2]}},{"geometry":{"type":"Polygon","coordinates":[[[20.59023,41.85541],[20.71731,41.84711],[20.76216,42.05186],[21.3527,42.2068],[21.576636,42.245224],[21.91708,42.30364],[22.380526,42.32026],[22.881374,41.999297],[22.952377,41.337994],[22.76177,41.3048],[22.597308,41.130487],[22.055378,41.149866],[21.674161,40.931275],[21.02004,40.842727],[20.60518,41.08622],[20.46315,41.51509],[20.59023,41.85541]]]},"type":"Feature","id":"MKD","properties":{"name":"Macedonia","id":"MKD","centroid":null}},{"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-11.834208,14.799097],[-11.666078,15.388208],[-11.349095,15.411256],[-10.650791,15.132746],[-10.086846,15.330486],[-9.700255,15.264107],[-9.550238,15.486497],[-5.537744,15.50169],[-5.315277,16.201854],[-5.488523,16.325102],[-5.971129,20.640833],[-6.453787,24.956591],[-4.923337,24.974574],[-1.550055,22.792666],[1.823228,20.610809],[2.060991,20.142233],[2.683588,19.85623],[3.146661,19.693579],[3.158133,19.057364],[4.267419,19.155265],[4.27021,16.852227],[3.723422,16.184284],[3.638259,15.56812],[2.749993,15.409525],[1.385528,15.323561],[1.015783,14.968182],[0.374892,14.928908],[-0.266257,14.924309],[-0.515854,15.116158],[-1.066363,14.973815],[-2.001035,14.559008],[-2.191825,14.246418],[-2.967694,13.79815],[-3.103707,13.541267],[-3.522803,13.337662],[-4.006391,13.472485],[-4.280405,13.228444],[-4.427166,12.542646],[-5.220942,11.713859],[-5.197843,11.375146],[-5.470565,10.95127],[-5.404342,10.370737],[-5.816926,10.222555],[-6.050452,10.096361],[-6.205223,10.524061],[-6.493965,10.411303],[-6.666461,10.430811],[-6.850507,10.138994],[-7.622759,10.147236],[-7.89959,10.297382],[-8.029944,10.206535],[-8.335377,10.494812],[-8.282357,10.792597],[-8.407311,10.909257],[-8.620321,10.810891],[-8.581305,11.136246],[-8.376305,11.393646],[-8.786099,11.812561],[-8.905265,12.088358],[-9.127474,12.30806],[-9.327616,12.334286],[-9.567912,12.194243],[-9.890993,12.060479],[-10.165214,11.844084],[-10.593224,11.923975],[-10.87083,12.177887],[-11.036556,12.211245],[-11.297574,12.077971],[-11.456169,12.076834],[-11.513943,12.442988],[-11.467899,12.754519],[-11.553398,13.141214],[-11.927716,13.422075],[-12.124887,13.994727],[-12.17075,14.616834]]]},"type":"Feature","id":"MLI","properties":{"name":"Mali","id":"MLI","centroid":[-7.55,12.34]}},{"geometry":{"type":"Polygon","coordinates":[[[99.543309,20.186598],[98.959676,19.752981],[98.253724,19.708203],[97.797783,18.62708],[97.375896,18.445438],[97.859123,17.567946],[98.493761,16.837836],[98.903348,16.177824],[98.537376,15.308497],[98.192074,15.123703],[98.430819,14.622028],[99.097755,13.827503],[99.212012,13.269294],[99.196354,12.804748],[99.587286,11.892763],[99.038121,10.960546],[98.553551,9.93296],[98.457174,10.675266],[98.764546,11.441292],[98.428339,12.032987],[98.509574,13.122378],[98.103604,13.64046],[97.777732,14.837286],[97.597072,16.100568],[97.16454,16.928734],[96.505769,16.427241],[95.369352,15.71439],[94.808405,15.803454],[94.188804,16.037936],[94.533486,17.27724],[94.324817,18.213514],[93.540988,19.366493],[93.663255,19.726962],[93.078278,19.855145],[92.368554,20.670883],[92.303234,21.475485],[92.652257,21.324048],[92.672721,22.041239],[93.166128,22.27846],[93.060294,22.703111],[93.286327,23.043658],[93.325188,24.078556],[94.106742,23.850741],[94.552658,24.675238],[94.603249,25.162495],[95.155153,26.001307],[95.124768,26.573572],[96.419366,27.264589],[97.133999,27.083774],[97.051989,27.699059],[97.402561,27.882536],[97.327114,28.261583],[97.911988,28.335945],[98.246231,27.747221],[98.68269,27.508812],[98.712094,26.743536],[98.671838,25.918703],[97.724609,25.083637],[97.60472,23.897405],[98.660262,24.063286],[98.898749,23.142722],[99.531992,22.949039],[99.240899,22.118314],[99.983489,21.742937],[100.416538,21.558839],[101.150033,21.849984],[101.180005,21.436573],[100.329101,20.786122],[100.115988,20.41785],[99.543309,20.186598]]]},"type":"Feature","id":"MMR","properties":{"name":"Myanmar","id":"MMR","centroid":[96.2,16.45]}},{"geometry":{"type":"Polygon","coordinates":[[[19.801613,42.500093],[19.738051,42.688247],[19.30449,42.19574],[19.37177,41.87755],[19.16246,41.95502],[18.88214,42.28151],[18.45,42.48],[18.56,42.65],[18.70648,43.20011],[19.03165,43.43253],[19.21852,43.52384],[19.48389,43.35229],[19.63,43.21378],[19.95857,43.10604],[20.3398,42.89852],[20.25758,42.81275],[20.0707,42.58863],[19.801613,42.500093]]]},"type":"Feature","id":"MNE","properties":{"name":"Montenegro","id":"MNE","centroid":[19.28,42.47]}},{"geometry":{"type":"Polygon","coordinates":[[[87.751264,49.297198],[88.805567,49.470521],[90.713667,50.331812],[92.234712,50.802171],[93.104219,50.49529],[94.147566,50.480537],[94.815949,50.013433],[95.814028,49.977467],[97.259728,49.726061],[98.231762,50.422401],[97.82574,51.010995],[98.861491,52.047366],[99.981732,51.634006],[100.88948,51.516856],[102.065223,51.259921],[102.255909,50.510561],[103.676545,50.089966],[104.621552,50.275329],[105.886591,50.406019],[106.888804,50.274296],[107.868176,49.793705],[108.475167,49.282548],[109.402449,49.292961],[110.662011,49.130128],[111.581231,49.377968],[112.89774,49.543565],[114.362456,50.248303],[114.96211,50.140247],[115.485695,49.805177],[116.678801,49.888531],[116.191802,49.134598],[115.485282,48.135383],[115.742837,47.726545],[116.308953,47.85341],[117.295507,47.697709],[118.064143,48.06673],[118.866574,47.74706],[119.772824,47.048059],[119.66327,46.69268],[118.874326,46.805412],[117.421701,46.672733],[116.717868,46.388202],[115.985096,45.727235],[114.460332,45.339817],[113.463907,44.808893],[112.436062,45.011646],[111.873306,45.102079],[111.348377,44.457442],[111.667737,44.073176],[111.829588,43.743118],[111.129682,43.406834],[110.412103,42.871234],[109.243596,42.519446],[107.744773,42.481516],[106.129316,42.134328],[104.964994,41.59741],[104.522282,41.908347],[103.312278,41.907468],[101.83304,42.514873],[100.845866,42.663804],[99.515817,42.524691],[97.451757,42.74889],[96.349396,42.725635],[95.762455,43.319449],[95.306875,44.241331],[94.688929,44.352332],[93.480734,44.975472],[92.133891,45.115076],[90.94554,45.286073],[90.585768,45.719716],[90.970809,46.888146],[90.280826,47.693549],[88.854298,48.069082],[88.013832,48.599463],[87.751264,49.297198]]]},"type":"Feature","id":"MNG","properties":{"name":"Mongolia","id":"MNG","centroid":[106.55,47.55]}},{"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[35.312398,-11.439146],[36.514082,-11.720938],[36.775151,-11.594537],[37.471284,-11.568751],[37.827645,-11.268769],[38.427557,-11.285202],[39.52103,-10.896854],[40.316589,-10.317096],[40.478387,-10.765441],[40.437253,-11.761711],[40.560811,-12.639177],[40.59962,-14.201975],[40.775475,-14.691764],[40.477251,-15.406294],[40.089264,-16.100774],[39.452559,-16.720891],[38.538351,-17.101023],[37.411133,-17.586368],[36.281279,-18.659688],[35.896497,-18.84226],[35.1984,-19.552811],[34.786383,-19.784012],[34.701893,-20.497043],[35.176127,-21.254361],[35.373428,-21.840837],[35.385848,-22.14],[35.562546,-22.09],[35.533935,-23.070788],[35.371774,-23.535359],[35.60747,-23.706563],[35.458746,-24.12261],[35.040735,-24.478351],[34.215824,-24.816314],[33.01321,-25.357573],[32.574632,-25.727318],[32.660363,-26.148584],[32.915955,-26.215867],[32.83012,-26.742192],[32.071665,-26.73382],[31.985779,-26.29178],[31.837778,-25.843332],[31.752408,-25.484284],[31.930589,-24.369417],[31.670398,-23.658969],[31.191409,-22.25151],[32.244988,-21.116489],[32.508693,-20.395292],[32.659743,-20.30429],[32.772708,-19.715592],[32.611994,-19.419383],[32.654886,-18.67209],[32.849861,-17.979057],[32.847639,-16.713398],[32.328239,-16.392074],[31.852041,-16.319417],[31.636498,-16.07199],[31.173064,-15.860944],[30.338955,-15.880839],[30.274256,-15.507787],[30.179481,-14.796099],[33.214025,-13.97186],[33.7897,-14.451831],[34.064825,-14.35995],[34.459633,-14.61301],[34.517666,-15.013709],[34.307291,-15.478641],[34.381292,-16.18356],[35.03381,-16.8013],[35.339063,-16.10744],[35.771905,-15.896859],[35.686845,-14.611046],[35.267956,-13.887834],[34.907151,-13.565425],[34.559989,-13.579998],[34.280006,-12.280025],[34.559989,-11.52002]]]},"type":"Feature","id":"MOZ","properties":{"name":"Mozambique","id":"MOZ","centroid":[32.32,-25.58]}},{"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-12.830658,15.303692],[-13.435738,16.039383],[-14.099521,16.304302],[-14.577348,16.598264],[-15.135737,16.587282],[-15.623666,16.369337],[-16.12069,16.455663],[-16.463098,16.135036],[-16.549708,16.673892],[-16.270552,17.166963],[-16.146347,18.108482],[-16.256883,19.096716],[-16.377651,19.593817],[-16.277838,20.092521],[-16.536324,20.567866],[-17.063423,20.999752],[-16.845194,21.333323],[-12.929102,21.327071],[-13.118754,22.77122],[-12.874222,23.284832],[-11.937224,23.374594],[-11.969419,25.933353],[-8.687294,25.881056],[-8.6844,27.395744],[-4.923337,24.974574],[-6.453787,24.956591],[-5.971129,20.640833],[-5.488523,16.325102],[-5.315277,16.201854],[-5.537744,15.50169],[-9.550238,15.486497],[-9.700255,15.264107],[-10.086846,15.330486],[-10.650791,15.132746],[-11.349095,15.411256],[-11.666078,15.388208],[-11.834208,14.799097],[-12.17075,14.616834]]]},"type":"Feature","id":"MRT","properties":{"name":"Mauritania","id":"MRT","centroid":[-15.58,18.04]}},{"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[34.280006,-12.280025],[34.559989,-13.579998],[34.907151,-13.565425],[35.267956,-13.887834],[35.686845,-14.611046],[35.771905,-15.896859],[35.339063,-16.10744],[35.03381,-16.8013],[34.381292,-16.18356],[34.307291,-15.478641],[34.517666,-15.013709],[34.459633,-14.61301],[34.064825,-14.35995],[33.7897,-14.451831],[33.214025,-13.97186],[32.688165,-13.712858],[32.991764,-12.783871],[33.306422,-12.435778],[33.114289,-11.607198],[33.31531,-10.79655],[33.485688,-10.525559],[33.231388,-9.676722],[32.759375,-9.230599],[33.739729,-9.417151],[33.940838,-9.693674],[34.280006,-10.16],[34.559989,-11.52002]]]},"type":"Feature","id":"MWI","properties":{"name":"Malawi","id":"MWI","centroid":[33.48,-14]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[111.38925781250006,2.415332031250031],[111.31152343749997,2.437597656250034],[111.33349609374997,2.768310546875],[111.38925781250006,2.415332031250031]]],[[[104.22158203125,2.731738281250003],[104.1291015625001,2.767236328125037],[104.18476562500004,2.871728515625009],[104.22158203125,2.731738281250003]]],[[[117.88476562499997,4.186132812500006],[117.64902343750012,4.168994140624974],[117.70800781249997,4.262402343749997],[117.88476562499997,4.186132812500006]]],[[[100.28896484375005,5.294726562499989],[100.19101562500006,5.28286132812498],[100.2455078125,5.467773437499986],[100.33886718749997,5.410058593750037],[100.28896484375005,5.294726562499989]]],[[[99.848046875,6.465722656249994],[99.9186523437501,6.358593750000011],[99.74375,6.263281249999963],[99.64628906250002,6.418359375000023],[99.848046875,6.465722656249994]]],[[[102.10107421874997,6.242236328125031],[102.34013671875002,6.172021484375023],[102.534375,5.862548828125028],[103.09707031250005,5.408447265624986],[103.41582031250007,4.85029296875004],[103.43945312499997,2.93310546875],[103.8122070312501,2.58046875],[104.21855468750002,1.722851562499997],[104.25009765625012,1.388574218750009],[104.11494140625004,1.412255859375037],[103.98144531250003,1.623632812500034],[103.99150390625002,1.454785156249997],[103.6945312500001,1.449658203125026],[103.48027343750007,1.329492187499966],[103.35683593750005,1.546142578125057],[102.72714843750012,1.855566406250034],[101.29550781250012,2.885205078125011],[101.29990234375012,3.253271484375034],[100.71542968750006,3.966210937499966],[100.79550781250012,4.023388671874983],[100.61455078125002,4.3734375],[100.34326171874997,5.984179687500031],[100.11914062499997,6.441992187500048],[100.26142578125004,6.682714843749963],[100.3454101562501,6.549902343750006],[100.75449218750012,6.460058593749991],[100.87392578125,6.24541015624996],[101.05351562500002,6.242578125],[100.98164062500004,5.771044921875045],[101.1139648437501,5.636767578125045],[101.5560546875,5.907763671875003],[101.67841796875004,5.778808593750028],[101.87363281250012,5.825292968749991],[102.10107421874997,6.242236328125031]]],[[[117.5744140625001,4.17060546875004],[117.10058593750003,4.337060546875023],[116.51474609375006,4.370800781249969],[115.86074218750005,4.348046875000037],[115.67880859375006,4.193017578124994],[115.45439453125002,3.034326171875009],[115.24697265625005,3.025927734374989],[115.117578125,2.89487304687502],[115.08076171875004,2.63422851562504],[115.1791015625,2.523193359374972],[114.78642578125002,2.250488281250014],[114.83056640625003,1.980029296874989],[114.5125,1.452001953124963],[113.90234375000003,1.434277343749997],[113.6222656250001,1.2359375],[113.00654296875004,1.433886718750003],[112.94296875000006,1.566992187500034],[112.47617187500006,1.559082031250028],[112.1857421875001,1.4390625],[112.078515625,1.143359374999974],[111.80898437500005,1.011669921874969],[111.10136718750002,1.050537109374986],[110.50576171875005,0.861962890625023],[109.65400390625004,1.614892578125023],[109.53896484375,1.89619140625004],[109.62890625000003,2.027539062499983],[109.86484375000012,1.764453125000031],[110.34921875000012,1.719726562499972],[111.22324218750012,1.395849609374991],[111.0287109375,1.557812500000026],[111.26816406250012,2.13974609375002],[111.20859375000012,2.379638671875043],[111.44384765625003,2.381542968749983],[111.5125,2.743017578124991],[112.98789062500006,3.161914062499974],[113.92392578125006,4.243212890625003],[114.0638671875,4.592675781249966],[114.65410156250007,4.037646484375045],[114.84023437500005,4.393212890625009],[114.74667968750006,4.718066406250017],[115.02675781250005,4.899707031249989],[115.10703125000006,4.390429687499974],[115.290625,4.352587890624989],[115.1400390625,4.899755859374991],[115.37490234375,4.932763671874966],[115.55449218750007,5.093554687500045],[115.41904296875012,5.413183593749963],[115.60390625,5.603417968749994],[115.74082031250012,5.533007812500045],[115.8771484375001,5.613525390625014],[116.74980468750007,6.977099609374989],[116.8498046875001,6.826708984374989],[116.78808593749997,6.606103515624994],[117.12851562500012,6.968896484375009],[117.2298828125,6.939990234374974],[117.29404296875006,6.676904296875023],[117.60966796875002,6.512646484375054],[117.69375,6.35],[117.64453124999997,6.001855468749994],[117.5011718750001,5.884667968750009],[118.00380859375,6.053320312499991],[118.11582031250006,5.8625],[117.93476562500004,5.7875],[117.97363281249997,5.70625],[118.35312500000012,5.80605468749998],[118.59482421875006,5.592089843750003],[119.22343750000007,5.412646484375031],[119.2663085937501,5.308105468750057],[119.21962890625,5.159814453125037],[118.9125,5.02290039062504],[118.26054687500007,4.988867187500034],[118.18535156250002,4.828515625000051],[118.5625,4.502148437499997],[118.54833984375003,4.379248046875006],[118.008203125,4.250244140625014],[117.6964843750001,4.342822265625045],[117.5744140625001,4.17060546875004]]],[[[117.14160156250003,7.168212890625028],[117.08066406250006,7.115283203124989],[117.06425781250007,7.26069335937504],[117.2640625,7.351660156250006],[117.26679687500004,7.220800781249991],[117.14160156250003,7.168212890625028]]]]},"type":"Feature","id":"MYS","properties":{"name":"Malaysia","id":"MYS","centroid":[101.41,3.09]}},{"geometry":{"type":"Polygon","coordinates":[[[16.344977,-28.576705],[15.601818,-27.821247],[15.210472,-27.090956],[14.989711,-26.117372],[14.743214,-25.39292],[14.408144,-23.853014],[14.385717,-22.656653],[14.257714,-22.111208],[13.868642,-21.699037],[13.352498,-20.872834],[12.826845,-19.673166],[12.608564,-19.045349],[11.794919,-18.069129],[11.734199,-17.301889],[12.215461,-17.111668],[12.814081,-16.941343],[13.462362,-16.971212],[14.058501,-17.423381],[14.209707,-17.353101],[18.263309,-17.309951],[18.956187,-17.789095],[21.377176,-17.930636],[23.215048,-17.523116],[24.033862,-17.295843],[24.682349,-17.353411],[25.07695,-17.578823],[25.084443,-17.661816],[24.520705,-17.887125],[24.217365,-17.889347],[23.579006,-18.281261],[23.196858,-17.869038],[21.65504,-18.219146],[20.910641,-18.252219],[20.881134,-21.814327],[19.895458,-21.849157],[19.895768,-24.76779],[19.894734,-28.461105],[19.002127,-28.972443],[18.464899,-29.045462],[17.836152,-28.856378],[17.387497,-28.783514],[17.218929,-28.355943],[16.824017,-28.082162],[16.344977,-28.576705]]]},"type":"Feature","id":"NAM","properties":{"name":"Namibia","id":"NAM","centroid":[17.04,-22.35]}},{"geometry":{"type":"Polygon","coordinates":[[[165.77999,-21.080005],[166.599991,-21.700019],[167.120011,-22.159991],[166.740035,-22.399976],[166.189732,-22.129708],[165.474375,-21.679607],[164.829815,-21.14982],[164.167995,-20.444747],[164.029606,-20.105646],[164.459967,-20.120012],[165.020036,-20.459991],[165.460009,-20.800022],[165.77999,-21.080005]]]},"type":"Feature","id":"NCL","properties":{"name":"New Caledonia","id":"NCL","centroid":[166.3,-22.17]}},{"geometry":{"type":"Polygon","coordinates":[[[2.154474,11.94015],[2.177108,12.625018],[1.024103,12.851826],[0.993046,13.33575],[0.429928,13.988733],[0.295646,14.444235],[0.374892,14.928908],[1.015783,14.968182],[1.385528,15.323561],[2.749993,15.409525],[3.638259,15.56812],[3.723422,16.184284],[4.27021,16.852227],[4.267419,19.155265],[5.677566,19.601207],[8.572893,21.565661],[11.999506,23.471668],[13.581425,23.040506],[14.143871,22.491289],[14.8513,22.86295],[15.096888,21.308519],[15.471077,21.048457],[15.487148,20.730415],[15.903247,20.387619],[15.685741,19.95718],[15.300441,17.92795],[15.247731,16.627306],[13.972202,15.684366],[13.540394,14.367134],[13.956699,13.996691],[13.954477,13.353449],[14.595781,13.330427],[14.495787,12.859396],[14.213531,12.802035],[14.181336,12.483657],[13.995353,12.461565],[13.318702,13.556356],[13.083987,13.596147],[12.302071,13.037189],[11.527803,13.32898],[10.989593,13.387323],[10.701032,13.246918],[10.114814,13.277252],[9.524928,12.851102],[9.014933,12.826659],[7.804671,13.343527],[7.330747,13.098038],[6.820442,13.115091],[6.445426,13.492768],[5.443058,13.865924],[4.368344,13.747482],[4.107946,13.531216],[3.967283,12.956109],[3.680634,12.552903],[3.61118,11.660167],[2.848643,12.235636],[2.490164,12.233052],[2.154474,11.94015]]]},"type":"Feature","id":"NER","properties":{"name":"Niger","id":"NER","centroid":[2.06,13.27]}},{"geometry":{"type":"Polygon","coordinates":[[[8.500288,4.771983],[7.462108,4.412108],[7.082596,4.464689],[6.698072,4.240594],[5.898173,4.262453],[5.362805,4.887971],[5.033574,5.611802],[4.325607,6.270651],[3.57418,6.2583],[2.691702,6.258817],[2.749063,7.870734],[2.723793,8.506845],[2.912308,9.137608],[3.220352,9.444153],[3.705438,10.06321],[3.60007,10.332186],[3.797112,10.734746],[3.572216,11.327939],[3.61118,11.660167],[3.680634,12.552903],[3.967283,12.956109],[4.107946,13.531216],[4.368344,13.747482],[5.443058,13.865924],[6.445426,13.492768],[6.820442,13.115091],[7.330747,13.098038],[7.804671,13.343527],[9.014933,12.826659],[9.524928,12.851102],[10.114814,13.277252],[10.701032,13.246918],[10.989593,13.387323],[11.527803,13.32898],[12.302071,13.037189],[13.083987,13.596147],[13.318702,13.556356],[13.995353,12.461565],[14.181336,12.483657],[14.577178,12.085361],[14.468192,11.904752],[14.415379,11.572369],[13.57295,10.798566],[13.308676,10.160362],[13.1676,9.640626],[12.955468,9.417772],[12.753672,8.717763],[12.218872,8.305824],[12.063946,7.799808],[11.839309,7.397042],[11.745774,6.981383],[11.058788,6.644427],[10.497375,7.055358],[10.118277,7.03877],[9.522706,6.453482],[9.233163,6.444491],[8.757533,5.479666],[8.500288,4.771983]]]},"type":"Feature","id":"NGA","properties":{"name":"Nigeria","id":"NGA","centroid":[7.32,9.05]}},{"geometry":{"type":"Polygon","coordinates":[[[-85.71254,11.088445],[-86.058488,11.403439],[-86.52585,11.806877],[-86.745992,12.143962],[-87.167516,12.458258],[-87.668493,12.90991],[-87.557467,13.064552],[-87.392386,12.914018],[-87.316654,12.984686],[-87.005769,13.025794],[-86.880557,13.254204],[-86.733822,13.263093],[-86.755087,13.754845],[-86.520708,13.778487],[-86.312142,13.771356],[-86.096264,14.038187],[-85.801295,13.836055],[-85.698665,13.960078],[-85.514413,14.079012],[-85.165365,14.35437],[-85.148751,14.560197],[-85.052787,14.551541],[-84.924501,14.790493],[-84.820037,14.819587],[-84.649582,14.666805],[-84.449336,14.621614],[-84.228342,14.748764],[-83.975721,14.749436],[-83.628585,14.880074],[-83.489989,15.016267],[-83.147219,14.995829],[-83.233234,14.899866],[-83.284162,14.676624],[-83.182126,14.310703],[-83.4125,13.970078],[-83.519832,13.567699],[-83.552207,13.127054],[-83.498515,12.869292],[-83.473323,12.419087],[-83.626104,12.32085],[-83.719613,11.893124],[-83.650858,11.629032],[-83.85547,11.373311],[-83.808936,11.103044],[-83.655612,10.938764],[-83.895054,10.726839],[-84.190179,10.79345],[-84.355931,10.999226],[-84.673069,11.082657],[-84.903003,10.952303],[-85.561852,11.217119],[-85.71254,11.088445]]]},"type":"Feature","id":"NIC","properties":{"name":"Nicaragua","id":"NIC","centroid":[-86.2,12.06]}},{"geometry":{"type":"Polygon","coordinates":[[[6.074183,53.510403],[6.90514,53.482162],[7.092053,53.144043],[6.84287,52.22844],[6.589397,51.852029],[5.988658,51.851616],[6.156658,50.803721],[5.606976,51.037298],[4.973991,51.475024],[4.047071,51.267259],[3.314971,51.345755],[3.830289,51.620545],[4.705997,53.091798],[6.074183,53.510403]]]},"type":"Feature","id":"NLD","properties":{"name":"Netherlands","id":"NLD","centroid":[4.54,52.23]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[28.165547,71.185474],[31.293418,70.453788],[30.005435,70.186259],[31.101079,69.55808],[29.399581,69.156916],[28.59193,69.064777],[29.015573,69.766491],[27.732292,70.164193],[26.179622,69.825299],[25.689213,69.092114],[24.735679,68.649557],[23.66205,68.891247],[22.356238,68.841741],[21.244936,69.370443],[20.645593,69.106247],[20.025269,69.065139],[19.87856,68.407194],[17.993868,68.567391],[17.729182,68.010552],[16.768879,68.013937],[16.108712,67.302456],[15.108411,66.193867],[13.55569,64.787028],[13.919905,64.445421],[13.571916,64.049114],[12.579935,64.066219],[11.930569,63.128318],[11.992064,61.800362],[12.631147,61.293572],[12.300366,60.117933],[11.468272,59.432393],[11.027369,58.856149],[10.356557,59.469807],[8.382,58.313288],[7.048748,58.078884],[5.665835,58.588155],[5.308234,59.663232],[4.992078,61.970998],[5.9129,62.614473],[8.553411,63.454008],[10.527709,64.486038],[12.358347,65.879726],[14.761146,67.810642],[16.435927,68.563205],[19.184028,69.817444],[21.378416,70.255169],[23.023742,70.202072],[24.546543,71.030497],[26.37005,70.986262],[28.165547,71.185474]]],[[[24.72412,77.85385],[22.49032,77.44493],[20.72601,77.67704],[21.41611,77.93504],[20.8119,78.25463],[22.88426,78.45494],[23.28134,78.07954],[24.72412,77.85385]]],[[[18.25183,79.70175],[21.54383,78.95611],[19.02737,78.5626],[18.47172,77.82669],[17.59441,77.63796],[17.1182,76.80941],[15.91315,76.77045],[13.76259,77.38035],[14.66956,77.73565],[13.1706,78.02493],[11.22231,78.8693],[10.44453,79.65239],[13.17077,80.01046],[13.71852,79.66039],[15.14282,79.67431],[15.52255,80.01608],[16.99085,80.05086],[18.25183,79.70175]]],[[[25.447625,80.40734],[27.407506,80.056406],[25.924651,79.517834],[23.024466,79.400012],[20.075188,79.566823],[19.897266,79.842362],[18.462264,79.85988],[17.368015,80.318896],[20.455992,80.598156],[21.907945,80.357679],[22.919253,80.657144],[25.447625,80.40734]]]]},"type":"Feature","id":"NOR","properties":{"name":"Norway","id":"NOR","centroid":[10.45,59.55]}},{"geometry":{"type":"Polygon","coordinates":[[[88.120441,27.876542],[88.043133,27.445819],[88.174804,26.810405],[88.060238,26.414615],[87.227472,26.397898],[86.024393,26.630985],[85.251779,26.726198],[84.675018,27.234901],[83.304249,27.364506],[81.999987,27.925479],[81.057203,28.416095],[80.088425,28.79447],[80.476721,29.729865],[81.111256,30.183481],[81.525804,30.422717],[82.327513,30.115268],[83.337115,29.463732],[83.898993,29.320226],[84.23458,28.839894],[85.011638,28.642774],[85.82332,28.203576],[86.954517,27.974262],[88.120441,27.876542]]]},"type":"Feature","id":"NPL","properties":{"name":"Nepal","id":"NPL","centroid":[85.2,27.45]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[173.020375,-40.919052],[173.247234,-41.331999],[173.958405,-40.926701],[174.247587,-41.349155],[174.248517,-41.770008],[173.876447,-42.233184],[173.22274,-42.970038],[172.711246,-43.372288],[173.080113,-43.853344],[172.308584,-43.865694],[171.452925,-44.242519],[171.185138,-44.897104],[170.616697,-45.908929],[169.831422,-46.355775],[169.332331,-46.641235],[168.411354,-46.619945],[167.763745,-46.290197],[166.676886,-46.219917],[166.509144,-45.852705],[167.046424,-45.110941],[168.303763,-44.123973],[168.949409,-43.935819],[169.667815,-43.555326],[170.52492,-43.031688],[171.12509,-42.512754],[171.569714,-41.767424],[171.948709,-41.514417],[172.097227,-40.956104],[172.79858,-40.493962],[173.020375,-40.919052]]],[[[174.612009,-36.156397],[175.336616,-37.209098],[175.357596,-36.526194],[175.808887,-36.798942],[175.95849,-37.555382],[176.763195,-37.881253],[177.438813,-37.961248],[178.010354,-37.579825],[178.517094,-37.695373],[178.274731,-38.582813],[177.97046,-39.166343],[177.206993,-39.145776],[176.939981,-39.449736],[177.032946,-39.879943],[176.885824,-40.065978],[176.508017,-40.604808],[176.01244,-41.289624],[175.239567,-41.688308],[175.067898,-41.425895],[174.650973,-41.281821],[175.22763,-40.459236],[174.900157,-39.908933],[173.824047,-39.508854],[173.852262,-39.146602],[174.574802,-38.797683],[174.743474,-38.027808],[174.697017,-37.381129],[174.292028,-36.711092],[174.319004,-36.534824],[173.840997,-36.121981],[173.054171,-35.237125],[172.636005,-34.529107],[173.007042,-34.450662],[173.551298,-35.006183],[174.32939,-35.265496],[174.612009,-36.156397]]]]},"type":"Feature","id":"NZL","properties":{"name":"New Zealand","id":"NZL","centroid":[174.46,-41.19]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[58.861141,21.114035],[58.487986,20.428986],[58.034318,20.481437],[57.826373,20.243002],[57.665762,19.736005],[57.7887,19.06757],[57.694391,18.94471],[57.234264,18.947991],[56.609651,18.574267],[56.512189,18.087113],[56.283521,17.876067],[55.661492,17.884128],[55.269939,17.632309],[55.2749,17.228354],[54.791002,16.950697],[54.239253,17.044981],[53.570508,16.707663],[53.108573,16.651051],[52.782184,17.349742],[52.00001,19.000003],[54.999982,19.999994],[55.666659,22.000001],[55.208341,22.70833],[55.234489,23.110993],[55.525841,23.524869],[55.528632,23.933604],[55.981214,24.130543],[55.804119,24.269604],[55.886233,24.920831],[56.396847,24.924732],[56.84514,24.241673],[57.403453,23.878594],[58.136948,23.747931],[58.729211,23.565668],[59.180502,22.992395],[59.450098,22.660271],[59.80806,22.533612],[59.806148,22.310525],[59.442191,21.714541],[59.282408,21.433886],[58.861141,21.114035]]],[[[56.391421,25.895991],[56.261042,25.714606],[56.070821,26.055464],[56.362017,26.395934],[56.485679,26.309118],[56.391421,25.895991]]]]},"type":"Feature","id":"OMN","properties":{"name":"Oman","id":"OMN","centroid":[58.36,23.37]}},{"geometry":{"type":"Polygon","coordinates":[[[75.158028,37.133031],[75.896897,36.666806],[76.192848,35.898403],[77.837451,35.49401],[76.871722,34.653544],[75.757061,34.504923],[74.240203,34.748887],[73.749948,34.317699],[74.104294,33.441473],[74.451559,32.7649],[75.258642,32.271105],[74.405929,31.692639],[74.42138,30.979815],[73.450638,29.976413],[72.823752,28.961592],[71.777666,27.91318],[70.616496,27.989196],[69.514393,26.940966],[70.168927,26.491872],[70.282873,25.722229],[70.844699,25.215102],[71.04324,24.356524],[68.842599,24.359134],[68.176645,23.691965],[67.443667,23.944844],[67.145442,24.663611],[66.372828,25.425141],[64.530408,25.237039],[62.905701,25.218409],[61.497363,25.078237],[61.874187,26.239975],[63.316632,26.756532],[63.233898,27.217047],[62.755426,27.378923],[62.72783,28.259645],[61.771868,28.699334],[61.369309,29.303276],[60.874248,29.829239],[62.549857,29.318572],[63.550261,29.468331],[64.148002,29.340819],[64.350419,29.560031],[65.046862,29.472181],[66.346473,29.887943],[66.381458,30.738899],[66.938891,31.304911],[67.683394,31.303154],[67.792689,31.58293],[68.556932,31.71331],[68.926677,31.620189],[69.317764,31.901412],[69.262522,32.501944],[69.687147,33.105499],[70.323594,33.358533],[69.930543,34.02012],[70.881803,33.988856],[71.156773,34.348911],[71.115019,34.733126],[71.613076,35.153203],[71.498768,35.650563],[71.262348,36.074388],[71.846292,36.509942],[72.920025,36.720007],[74.067552,36.836176],[74.575893,37.020841],[75.158028,37.133031]]]},"type":"Feature","id":"PAK","properties":{"name":"Pakistan","id":"PAK","centroid":[73.1,33.4]}},{"geometry":{"type":"Polygon","coordinates":[[[-77.881571,7.223771],[-78.214936,7.512255],[-78.429161,8.052041],[-78.182096,8.319182],[-78.435465,8.387705],[-78.622121,8.718124],[-79.120307,8.996092],[-79.557877,8.932375],[-79.760578,8.584515],[-80.164481,8.333316],[-80.382659,8.298409],[-80.480689,8.090308],[-80.00369,7.547524],[-80.276671,7.419754],[-80.421158,7.271572],[-80.886401,7.220541],[-81.059543,7.817921],[-81.189716,7.647906],[-81.519515,7.70661],[-81.721311,8.108963],[-82.131441,8.175393],[-82.390934,8.292362],[-82.820081,8.290864],[-82.850958,8.073823],[-82.965783,8.225028],[-82.913176,8.423517],[-82.829771,8.626295],[-82.868657,8.807266],[-82.719183,8.925709],[-82.927155,9.07433],[-82.932891,9.476812],[-82.546196,9.566135],[-82.187123,9.207449],[-82.207586,8.995575],[-81.808567,8.950617],[-81.714154,9.031955],[-81.439287,8.786234],[-80.947302,8.858504],[-80.521901,9.111072],[-79.9146,9.312765],[-79.573303,9.61161],[-79.021192,9.552931],[-79.05845,9.454565],[-78.500888,9.420459],[-78.055928,9.24773],[-77.729514,8.946844],[-77.353361,8.670505],[-77.474723,8.524286],[-77.242566,7.935278],[-77.431108,7.638061],[-77.753414,7.70984],[-77.881571,7.223771]]]},"type":"Feature","id":"PAN","properties":{"name":"Panama","id":"PAN","centroid":[-79.25,9]}},{"geometry":{"type":"Polygon","coordinates":[[[-69.590424,-17.580012],[-69.858444,-18.092694],[-70.372572,-18.347975],[-71.37525,-17.773799],[-71.462041,-17.363488],[-73.44453,-16.359363],[-75.237883,-15.265683],[-76.009205,-14.649286],[-76.423469,-13.823187],[-76.259242,-13.535039],[-77.106192,-12.222716],[-78.092153,-10.377712],[-79.036953,-8.386568],[-79.44592,-7.930833],[-79.760578,-7.194341],[-80.537482,-6.541668],[-81.249996,-6.136834],[-80.926347,-5.690557],[-81.410943,-4.736765],[-81.09967,-4.036394],[-80.302561,-3.404856],[-80.184015,-3.821162],[-80.469295,-4.059287],[-80.442242,-4.425724],[-80.028908,-4.346091],[-79.624979,-4.454198],[-79.205289,-4.959129],[-78.639897,-4.547784],[-78.450684,-3.873097],[-77.837905,-3.003021],[-76.635394,-2.608678],[-75.544996,-1.56161],[-75.233723,-0.911417],[-75.373223,-0.152032],[-75.106625,-0.057205],[-74.441601,-0.53082],[-74.122395,-1.002833],[-73.659504,-1.260491],[-73.070392,-2.308954],[-72.325787,-2.434218],[-71.774761,-2.16979],[-71.413646,-2.342802],[-70.813476,-2.256865],[-70.047709,-2.725156],[-70.692682,-3.742872],[-70.394044,-3.766591],[-69.893635,-4.298187],[-70.794769,-4.251265],[-70.928843,-4.401591],[-71.748406,-4.593983],[-72.891928,-5.274561],[-72.964507,-5.741251],[-73.219711,-6.089189],[-73.120027,-6.629931],[-73.724487,-6.918595],[-73.723401,-7.340999],[-73.987235,-7.52383],[-73.571059,-8.424447],[-73.015383,-9.032833],[-73.226713,-9.462213],[-72.563033,-9.520194],[-72.184891,-10.053598],[-71.302412,-10.079436],[-70.481894,-9.490118],[-70.548686,-11.009147],[-70.093752,-11.123972],[-69.529678,-10.951734],[-68.66508,-12.5613],[-68.88008,-12.899729],[-68.929224,-13.602684],[-68.948887,-14.453639],[-69.339535,-14.953195],[-69.160347,-15.323974],[-69.389764,-15.660129],[-68.959635,-16.500698],[-69.590424,-17.580012]]]},"type":"Feature","id":"PER","properties":{"name":"Peru","id":"PER","centroid":[-77,-12]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[126.376814,8.414706],[126.478513,7.750354],[126.537424,7.189381],[126.196773,6.274294],[125.831421,7.293715],[125.363852,6.786485],[125.683161,6.049657],[125.396512,5.581003],[124.219788,6.161355],[123.93872,6.885136],[124.243662,7.36061],[123.610212,7.833527],[123.296071,7.418876],[122.825506,7.457375],[122.085499,6.899424],[121.919928,7.192119],[122.312359,8.034962],[122.942398,8.316237],[123.487688,8.69301],[123.841154,8.240324],[124.60147,8.514158],[124.764612,8.960409],[125.471391,8.986997],[125.412118,9.760335],[126.222714,9.286074],[126.306637,8.782487],[126.376814,8.414706]]],[[[123.982438,10.278779],[123.623183,9.950091],[123.309921,9.318269],[122.995883,9.022189],[122.380055,9.713361],[122.586089,9.981045],[122.837081,10.261157],[122.947411,10.881868],[123.49885,10.940624],[123.337774,10.267384],[124.077936,11.232726],[123.982438,10.278779]]],[[[118.504581,9.316383],[117.174275,8.3675],[117.664477,9.066889],[118.386914,9.6845],[118.987342,10.376292],[119.511496,11.369668],[119.689677,10.554291],[119.029458,10.003653],[118.504581,9.316383]]],[[[121.883548,11.891755],[122.483821,11.582187],[123.120217,11.58366],[123.100838,11.165934],[122.637714,10.741308],[122.00261,10.441017],[121.967367,10.905691],[122.03837,11.415841],[121.883548,11.891755]]],[[[125.502552,12.162695],[125.783465,11.046122],[125.011884,11.311455],[125.032761,10.975816],[125.277449,10.358722],[124.801819,10.134679],[124.760168,10.837995],[124.459101,10.88993],[124.302522,11.495371],[124.891013,11.415583],[124.87799,11.79419],[124.266762,12.557761],[125.227116,12.535721],[125.502552,12.162695]]],[[[121.527394,13.06959],[121.26219,12.20556],[120.833896,12.704496],[120.323436,13.466413],[121.180128,13.429697],[121.527394,13.06959]]],[[[121.321308,18.504065],[121.937601,18.218552],[122.246006,18.47895],[122.336957,18.224883],[122.174279,17.810283],[122.515654,17.093505],[122.252311,16.262444],[121.662786,15.931018],[121.50507,15.124814],[121.728829,14.328376],[122.258925,14.218202],[122.701276,14.336541],[123.950295,13.782131],[123.855107,13.237771],[124.181289,12.997527],[124.077419,12.536677],[123.298035,13.027526],[122.928652,13.55292],[122.671355,13.185836],[122.03465,13.784482],[121.126385,13.636687],[120.628637,13.857656],[120.679384,14.271016],[120.991819,14.525393],[120.693336,14.756671],[120.564145,14.396279],[120.070429,14.970869],[119.920929,15.406347],[119.883773,16.363704],[120.286488,16.034629],[120.390047,17.599081],[120.715867,18.505227],[121.321308,18.504065]]]]},"type":"Feature","id":"PHL","properties":{"name":"Philippines","id":"PHL","centroid":[121.03,14.4]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[155.880026,-6.819997],[155.599991,-6.919991],[155.166994,-6.535931],[154.729192,-5.900828],[154.514114,-5.139118],[154.652504,-5.042431],[154.759991,-5.339984],[155.062918,-5.566792],[155.547746,-6.200655],[156.019965,-6.540014],[155.880026,-6.819997]]],[[[151.982796,-5.478063],[151.459107,-5.56028],[151.30139,-5.840728],[150.754447,-6.083763],[150.241197,-6.317754],[149.709963,-6.316513],[148.890065,-6.02604],[148.318937,-5.747142],[148.401826,-5.437756],[149.298412,-5.583742],[149.845562,-5.505503],[149.99625,-5.026101],[150.139756,-5.001348],[150.236908,-5.53222],[150.807467,-5.455842],[151.089672,-5.113693],[151.647881,-4.757074],[151.537862,-4.167807],[152.136792,-4.14879],[152.338743,-4.312966],[152.318693,-4.867661],[151.982796,-5.478063]]],[[[147.191874,-7.388024],[148.084636,-8.044108],[148.734105,-9.104664],[149.306835,-9.071436],[149.266631,-9.514406],[150.038728,-9.684318],[149.738798,-9.872937],[150.801628,-10.293687],[150.690575,-10.582713],[150.028393,-10.652476],[149.78231,-10.393267],[148.923138,-10.280923],[147.913018,-10.130441],[147.135443,-9.492444],[146.567881,-8.942555],[146.048481,-8.067414],[144.744168,-7.630128],[143.897088,-7.91533],[143.286376,-8.245491],[143.413913,-8.983069],[142.628431,-9.326821],[142.068259,-9.159596],[141.033852,-9.117893],[141.017057,-5.859022],[141.00021,-2.600151],[142.735247,-3.289153],[144.583971,-3.861418],[145.27318,-4.373738],[145.829786,-4.876498],[145.981922,-5.465609],[147.648073,-6.083659],[147.891108,-6.614015],[146.970905,-6.721657],[147.191874,-7.388024]]],[[[153.140038,-4.499983],[152.827292,-4.766427],[152.638673,-4.176127],[152.406026,-3.789743],[151.953237,-3.462062],[151.384279,-3.035422],[150.66205,-2.741486],[150.939965,-2.500002],[151.479984,-2.779985],[151.820015,-2.999972],[152.239989,-3.240009],[152.640017,-3.659983],[153.019994,-3.980015],[153.140038,-4.499983]]]]},"type":"Feature","id":"PNG","properties":{"name":"Papua New Guinea","id":"PNG","centroid":[147.08,-9.24]}},{"geometry":{"type":"Polygon","coordinates":[[[15.016996,51.106674],[14.607098,51.745188],[14.685026,52.089947],[14.4376,52.62485],[14.074521,52.981263],[14.353315,53.248171],[14.119686,53.757029],[14.8029,54.050706],[16.363477,54.513159],[17.622832,54.851536],[18.620859,54.682606],[18.696255,54.438719],[19.66064,54.426084],[20.892245,54.312525],[22.731099,54.327537],[23.243987,54.220567],[23.484128,53.912498],[23.527536,53.470122],[23.804935,53.089731],[23.799199,52.691099],[23.199494,52.486977],[23.508002,52.023647],[23.527071,51.578454],[24.029986,50.705407],[23.922757,50.424881],[23.426508,50.308506],[22.51845,49.476774],[22.776419,49.027395],[22.558138,49.085738],[21.607808,49.470107],[20.887955,49.328772],[20.415839,49.431453],[19.825023,49.217125],[19.320713,49.571574],[18.909575,49.435846],[18.853144,49.49623],[18.392914,49.988629],[17.649445,50.049038],[17.554567,50.362146],[16.868769,50.473974],[16.719476,50.215747],[16.176253,50.422607],[16.238627,50.697733],[15.490972,50.78473],[15.016996,51.106674]]]},"type":"Feature","id":"POL","properties":{"name":"Poland","id":"POL","centroid":[21,52.13]}},{"geometry":{"type":"Polygon","coordinates":[[[-66.282434,18.514762],[-65.771303,18.426679],[-65.591004,18.228035],[-65.847164,17.975906],[-66.599934,17.981823],[-67.184162,17.946553],[-67.242428,18.37446],[-67.100679,18.520601],[-66.282434,18.514762]]]},"type":"Feature","id":"PRI","properties":{"name":"Puerto Rico","id":"PRI","centroid":[-66.07,18.28]}},{"geometry":{"type":"Polygon","coordinates":[[[130.640016,42.395009],[130.780007,42.220007],[130.400031,42.280004],[129.965949,41.941368],[129.667362,41.601104],[129.705189,40.882828],[129.188115,40.661808],[129.0104,40.485436],[128.633368,40.189847],[127.967414,40.025413],[127.533436,39.75685],[127.50212,39.323931],[127.385434,39.213472],[127.783343,39.050898],[128.349716,38.612243],[128.205746,38.370397],[127.780035,38.304536],[127.073309,38.256115],[126.68372,37.804773],[126.237339,37.840378],[126.174759,37.749686],[125.689104,37.94001],[125.568439,37.752089],[125.27533,37.669071],[125.240087,37.857224],[124.981033,37.948821],[124.712161,38.108346],[124.985994,38.548474],[125.221949,38.665857],[125.132859,38.848559],[125.38659,39.387958],[125.321116,39.551385],[124.737482,39.660344],[124.265625,39.928493],[125.079942,40.569824],[126.182045,41.107336],[126.869083,41.816569],[127.343783,41.503152],[128.208433,41.466772],[128.052215,41.994285],[129.596669,42.424982],[129.994267,42.985387],[130.640016,42.395009]]]},"type":"Feature","id":"PRK","properties":{"name":"North Korea","id":"PRK","centroid":[125.44,39.02]}},{"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.671946,42.134689],[-8.263857,42.280469],[-8.013175,41.790886],[-7.422513,41.792075],[-7.251309,41.918346],[-6.668606,41.883387],[-6.389088,41.381815],[-6.851127,41.111083],[-6.86402,40.330872],[-7.026413,40.184524],[-7.066592,39.711892],[-7.498632,39.629571],[-7.098037,39.030073],[-7.374092,38.373059],[-7.029281,38.075764],[-7.166508,37.803894],[-7.537105,37.428904],[-7.453726,37.097788],[-7.855613,36.838269],[-8.382816,36.97888],[-8.898857,36.868809],[-8.746101,37.651346],[-8.839998,38.266243],[-9.287464,38.358486],[-9.526571,38.737429],[-9.446989,39.392066],[-9.048305,39.755093],[-8.977353,40.159306],[-8.768684,40.760639],[-8.790853,41.184334],[-8.990789,41.543459],[-9.034818,41.880571]]]},"type":"Feature","id":"PRT","properties":{"name":"Portugal","id":"PRT","centroid":[-9.1,38.42]}},{"geometry":{"type":"Polygon","coordinates":[[[-62.685057,-22.249029],[-62.291179,-21.051635],[-62.265961,-20.513735],[-61.786326,-19.633737],[-60.043565,-19.342747],[-59.115042,-19.356906],[-58.183471,-19.868399],[-58.166392,-20.176701],[-57.870674,-20.732688],[-57.937156,-22.090176],[-56.88151,-22.282154],[-56.473317,-22.0863],[-55.797958,-22.35693],[-55.610683,-22.655619],[-55.517639,-23.571998],[-55.400747,-23.956935],[-55.027902,-24.001274],[-54.652834,-23.839578],[-54.29296,-24.021014],[-54.293476,-24.5708],[-54.428946,-25.162185],[-54.625291,-25.739255],[-54.788795,-26.621786],[-55.695846,-27.387837],[-56.486702,-27.548499],[-57.60976,-27.395899],[-58.618174,-27.123719],[-57.63366,-25.603657],[-57.777217,-25.16234],[-58.807128,-24.771459],[-60.028966,-24.032796],[-60.846565,-23.880713],[-62.685057,-22.249029]]]},"type":"Feature","id":"PRY","properties":{"name":"Paraguay","id":"PRY","centroid":[-57.38,-25.18]}},{"geometry":{"type":"Polygon","coordinates":[[[50.810108,24.754743],[50.743911,25.482424],[51.013352,26.006992],[51.286462,26.114582],[51.589079,25.801113],[51.6067,25.21567],[51.389608,24.627386],[51.112415,24.556331],[50.810108,24.754743]]]},"type":"Feature","id":"QAT","properties":{"name":"Qatar","id":"QAT","centroid":[51.35,25.15]}},{"geometry":{"type":"Polygon","coordinates":[[[22.710531,47.882194],[23.142236,48.096341],[23.760958,47.985598],[24.402056,47.981878],[24.866317,47.737526],[25.207743,47.891056],[25.945941,47.987149],[26.19745,48.220881],[26.619337,48.220726],[26.924176,48.123264],[27.233873,47.826771],[27.551166,47.405117],[28.12803,46.810476],[28.160018,46.371563],[28.054443,45.944586],[28.233554,45.488283],[28.679779,45.304031],[29.149725,45.464925],[29.603289,45.293308],[29.626543,45.035391],[29.141612,44.82021],[28.837858,44.913874],[28.558081,43.707462],[27.970107,43.812468],[27.2424,44.175986],[26.065159,43.943494],[25.569272,43.688445],[24.100679,43.741051],[23.332302,43.897011],[22.944832,43.823785],[22.65715,44.234923],[22.474008,44.409228],[22.705726,44.578003],[22.459022,44.702517],[22.145088,44.478422],[21.562023,44.768947],[21.483526,45.18117],[20.874313,45.416375],[20.762175,45.734573],[20.220192,46.127469],[21.021952,46.316088],[21.626515,46.994238],[22.099768,47.672439],[22.710531,47.882194]]]},"type":"Feature","id":"ROU","properties":{"name":"Romania","id":"ROU","centroid":[26.1,44.27]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[143.648007,50.7476],[144.654148,48.976391],[143.173928,49.306551],[142.558668,47.861575],[143.533492,46.836728],[143.505277,46.137908],[142.747701,46.740765],[142.09203,45.966755],[141.906925,46.805929],[142.018443,47.780133],[141.904445,48.859189],[142.1358,49.615163],[142.179983,50.952342],[141.594076,51.935435],[141.682546,53.301966],[142.606934,53.762145],[142.209749,54.225476],[142.654786,54.365881],[142.914616,53.704578],[143.260848,52.74076],[143.235268,51.75666],[143.648007,50.7476]]],[[[22.731099,54.327537],[20.892245,54.312525],[19.66064,54.426084],[19.888481,54.86616],[21.268449,55.190482],[22.315724,55.015299],[22.757764,54.856574],[22.651052,54.582741],[22.731099,54.327537]]],[[[-175.01425,66.58435],[-174.33983,66.33556],[-174.57182,67.06219],[-171.85731,66.91308],[-169.89958,65.97724],[-170.89107,65.54139],[-172.53025,65.43791],[-172.555,64.46079],[-172.95533,64.25269],[-173.89184,64.2826],[-174.65392,64.63125],[-175.98353,64.92288],[-176.20716,65.35667],[-177.22266,65.52024],[-178.35993,65.39052],[-178.90332,65.74044],[-178.68611,66.11211],[-179.88377,65.87456],[-179.43268,65.40411],[-180,64.979709],[-180,68.963636],[-177.55,68.2],[-174.92825,67.20589],[-175.01425,66.58435]]],[[[180,70.832199],[178.903425,70.78114],[178.7253,71.0988],[180,71.515714],[180,70.832199]]],[[[-178.69378,70.89302],[-180,70.832199],[-180,71.515714],[-179.871875,71.55762],[-179.02433,71.55553],[-177.577945,71.26948],[-177.663575,71.13277],[-178.69378,70.89302]]],[[[143.60385,73.21244],[142.08763,73.20544],[140.038155,73.31692],[139.86312,73.36983],[140.81171,73.76506],[142.06207,73.85758],[143.48283,73.47525],[143.60385,73.21244]]],[[[150.73167,75.08406],[149.575925,74.68892],[147.977465,74.778355],[146.11919,75.17298],[146.358485,75.49682],[148.22223,75.345845],[150.73167,75.08406]]],[[[145.086285,75.562625],[144.3,74.82],[140.61381,74.84768],[138.95544,74.61148],[136.97439,75.26167],[137.51176,75.94917],[138.831075,76.13676],[141.471615,76.09289],[145.086285,75.562625]]],[[[57.535693,70.720464],[56.944979,70.632743],[53.677375,70.762658],[53.412017,71.206662],[51.601895,71.474759],[51.455754,72.014881],[52.478275,72.229442],[52.444169,72.774731],[54.427614,73.627548],[53.50829,73.749814],[55.902459,74.627486],[55.631933,75.081412],[57.868644,75.60939],[61.170044,76.251883],[64.498368,76.439055],[66.210977,76.809782],[68.15706,76.939697],[68.852211,76.544811],[68.180573,76.233642],[64.637326,75.737755],[61.583508,75.260885],[58.477082,74.309056],[56.986786,73.333044],[55.419336,72.371268],[55.622838,71.540595],[57.535693,70.720464]]],[[[106.97013,76.97419],[107.24,76.48],[108.1538,76.72335],[111.07726,76.71],[113.33151,76.22224],[114.13417,75.84764],[113.88539,75.32779],[112.77918,75.03186],[110.15125,74.47673],[109.4,74.18],[110.64,74.04],[112.11919,73.78774],[113.01954,73.97693],[113.52958,73.33505],[113.96881,73.59488],[115.56782,73.75285],[118.77633,73.58772],[119.02,73.12],[123.20066,72.97122],[123.25777,73.73503],[125.38,73.56],[126.97644,73.56549],[128.59126,73.03871],[129.05157,72.39872],[128.46,71.98],[129.71599,71.19304],[131.28858,70.78699],[132.2535,71.8363],[133.85766,71.38642],[135.56193,71.65525],[137.49755,71.34763],[138.23409,71.62803],[139.86983,71.48783],[139.14791,72.41619],[140.46817,72.84941],[149.5,72.2],[150.35118,71.60643],[152.9689,70.84222],[157.00688,71.03141],[158.99779,70.86672],[159.83031,70.45324],[159.70866,69.72198],[160.94053,69.43728],[162.27907,69.64204],[164.05248,69.66823],[165.94037,69.47199],[167.83567,69.58269],[169.57763,68.6938],[170.81688,69.01363],[170.0082,69.65276],[170.45345,70.09703],[173.64391,69.81743],[175.72403,69.87725],[178.6,69.4],[180,68.963636],[180,64.979709],[179.99281,64.97433],[178.7072,64.53493],[177.41128,64.60821],[178.313,64.07593],[178.90825,63.25197],[179.37034,62.98262],[179.48636,62.56894],[179.22825,62.3041],[177.3643,62.5219],[174.56929,61.76915],[173.68013,61.65261],[172.15,60.95],[170.6985,60.33618],[170.33085,59.88177],[168.90046,60.57355],[166.29498,59.78855],[165.84,60.16],[164.87674,59.7316],[163.53929,59.86871],[163.21711,59.21101],[162.01733,58.24328],[162.05297,57.83912],[163.19191,57.61503],[163.05794,56.15924],[162.12958,56.12219],[161.70146,55.28568],[162.11749,54.85514],[160.36877,54.34433],[160.02173,53.20257],[158.53094,52.95868],[158.23118,51.94269],[156.78979,51.01105],[156.42,51.7],[155.99182,53.15895],[155.43366,55.38103],[155.91442,56.76792],[156.75815,57.3647],[156.81035,57.83204],[158.36433,58.05575],[160.15064,59.31477],[161.87204,60.343],[163.66969,61.1409],[164.47355,62.55061],[163.25842,62.46627],[162.65791,61.6425],[160.12148,60.54423],[159.30232,61.77396],[156.72068,61.43442],[154.21806,59.75818],[155.04375,59.14495],[152.81185,58.88385],[151.26573,58.78089],[151.33815,59.50396],[149.78371,59.65573],[148.54481,59.16448],[145.48722,59.33637],[142.19782,59.03998],[138.95848,57.08805],[135.12619,54.72959],[136.70171,54.60355],[137.19342,53.97732],[138.1647,53.75501],[138.80463,54.25455],[139.90151,54.18968],[141.34531,53.08957],[141.37923,52.23877],[140.59742,51.23967],[140.51308,50.04553],[140.06193,48.44671],[138.55472,46.99965],[138.21971,46.30795],[136.86232,45.1435],[135.51535,43.989],[134.86939,43.39821],[133.53687,42.81147],[132.90627,42.79849],[132.27807,43.28456],[130.93587,42.55274],[130.78,42.22],[130.64,42.395],[130.633866,42.903015],[131.144688,42.92999],[131.288555,44.11152],[131.02519,44.96796],[131.883454,45.321162],[133.09712,45.14409],[133.769644,46.116927],[134.11235,47.21248],[134.50081,47.57845],[135.026311,48.47823],[133.373596,48.183442],[132.50669,47.78896],[130.98726,47.79013],[130.582293,48.729687],[129.397818,49.4406],[127.6574,49.76027],[127.287456,50.739797],[126.939157,51.353894],[126.564399,51.784255],[125.946349,52.792799],[125.068211,53.161045],[123.57147,53.4588],[122.245748,53.431726],[121.003085,53.251401],[120.177089,52.753886],[120.725789,52.516226],[120.7382,51.96411],[120.18208,51.64355],[119.27939,50.58292],[119.288461,50.142883],[117.879244,49.510983],[116.678801,49.888531],[115.485695,49.805177],[114.96211,50.140247],[114.362456,50.248303],[112.89774,49.543565],[111.581231,49.377968],[110.662011,49.130128],[109.402449,49.292961],[108.475167,49.282548],[107.868176,49.793705],[106.888804,50.274296],[105.886591,50.406019],[104.62158,50.27532],[103.676545,50.089966],[102.25589,50.51056],[102.06521,51.25991],[100.88948,51.516856],[99.981732,51.634006],[98.861491,52.047366],[97.82574,51.010995],[98.231762,50.422401],[97.25976,49.72605],[95.81402,49.97746],[94.815949,50.013433],[94.147566,50.480537],[93.10421,50.49529],[92.234712,50.802171],[90.713667,50.331812],[88.805567,49.470521],[87.751264,49.297198],[87.35997,49.214981],[86.829357,49.826675],[85.54127,49.692859],[85.11556,50.117303],[84.416377,50.3114],[83.935115,50.889246],[83.383004,51.069183],[81.945986,50.812196],[80.568447,51.388336],[80.03556,50.864751],[77.800916,53.404415],[76.525179,54.177003],[76.8911,54.490524],[74.38482,53.54685],[73.425679,53.48981],[73.508516,54.035617],[72.22415,54.376655],[71.180131,54.133285],[70.865267,55.169734],[69.068167,55.38525],[68.1691,54.970392],[65.66687,54.60125],[65.178534,54.354228],[61.4366,54.00625],[60.978066,53.664993],[61.699986,52.979996],[60.739993,52.719986],[60.927269,52.447548],[59.967534,51.96042],[61.588003,51.272659],[61.337424,50.79907],[59.932807,50.842194],[59.642282,50.545442],[58.36332,51.06364],[56.77798,51.04355],[55.71694,50.62171],[54.532878,51.02624],[52.328724,51.718652],[50.766648,51.692762],[48.702382,50.605128],[48.577841,49.87476],[47.54948,50.454698],[46.751596,49.356006],[47.043672,49.152039],[46.466446,48.394152],[47.31524,47.71585],[48.05725,47.74377],[48.694734,47.075628],[48.59325,46.56104],[49.10116,46.39933],[48.64541,45.80629],[47.67591,45.64149],[46.68201,44.6092],[47.59094,43.66016],[47.49252,42.98658],[48.58437,41.80888],[47.987283,41.405819],[47.815666,41.151416],[47.373315,41.219732],[46.686071,41.827137],[46.404951,41.860675],[45.7764,42.09244],[45.470279,42.502781],[44.537623,42.711993],[43.93121,42.55496],[43.75599,42.74083],[42.3944,43.2203],[40.92219,43.38215],[40.076965,43.553104],[39.955009,43.434998],[38.68,44.28],[37.53912,44.65721],[36.67546,45.24469],[37.40317,45.40451],[38.23295,46.24087],[37.67372,46.63657],[39.14767,47.04475],[39.1212,47.26336],[38.223538,47.10219],[38.255112,47.5464],[38.77057,47.82562],[39.738278,47.898937],[39.89562,48.23241],[39.67465,48.78382],[40.080789,49.30743],[40.06904,49.60105],[38.594988,49.926462],[38.010631,49.915662],[37.39346,50.383953],[36.626168,50.225591],[35.356116,50.577197],[35.37791,50.77394],[35.022183,51.207572],[34.224816,51.255993],[34.141978,51.566413],[34.391731,51.768882],[33.7527,52.335075],[32.715761,52.238465],[32.412058,52.288695],[32.15944,52.06125],[31.78597,52.10168],[31.540018,52.742052],[31.305201,53.073996],[31.49764,53.16743],[32.304519,53.132726],[32.693643,53.351421],[32.405599,53.618045],[31.731273,53.794029],[31.791424,53.974639],[31.384472,54.157056],[30.757534,54.811771],[30.971836,55.081548],[30.873909,55.550976],[29.896294,55.789463],[29.371572,55.670091],[29.229513,55.918344],[28.176709,56.16913],[27.855282,56.759326],[27.770016,57.244258],[27.288185,57.474528],[27.716686,57.791899],[27.42015,58.72457],[28.131699,59.300825],[27.98112,59.47537],[29.1177,60.02805],[28.07,60.50352],[30.211107,61.780028],[31.139991,62.357693],[31.516092,62.867687],[30.035872,63.552814],[30.444685,64.204453],[29.54443,64.948672],[30.21765,65.80598],[29.054589,66.944286],[29.977426,67.698297],[28.445944,68.364613],[28.59193,69.064777],[29.39955,69.15692],[31.10108,69.55811],[32.13272,69.90595],[33.77547,69.30142],[36.51396,69.06342],[40.29234,67.9324],[41.05987,67.45713],[41.12595,66.79158],[40.01583,66.26618],[38.38295,65.99953],[33.91871,66.75961],[33.18444,66.63253],[34.81477,65.90015],[34.878574,65.436213],[34.94391,64.41437],[36.23129,64.10945],[37.01273,63.84983],[37.14197,64.33471],[36.539579,64.76446],[37.17604,65.14322],[39.59345,64.52079],[40.4356,64.76446],[39.7626,65.49682],[42.09309,66.47623],[43.01604,66.41858],[43.94975,66.06908],[44.53226,66.75634],[43.69839,67.35245],[44.18795,67.95051],[43.45282,68.57079],[46.25,68.25],[46.82134,67.68997],[45.55517,67.56652],[45.56202,67.01005],[46.34915,66.66767],[47.89416,66.88455],[48.13876,67.52238],[50.22766,67.99867],[53.71743,68.85738],[54.47171,68.80815],[53.48582,68.20131],[54.72628,68.09702],[55.44268,68.43866],[57.31702,68.46628],[58.802,68.88082],[59.94142,68.27844],[61.07784,68.94069],[60.03,69.52],[60.55,69.85],[63.504,69.54739],[64.888115,69.234835],[68.51216,68.09233],[69.18068,68.61563],[68.16444,69.14436],[68.13522,69.35649],[66.93008,69.45461],[67.25976,69.92873],[66.72492,70.70889],[66.69466,71.02897],[68.54006,71.9345],[69.19636,72.84336],[69.94,73.04],[72.58754,72.77629],[72.79603,72.22006],[71.84811,71.40898],[72.47011,71.09019],[72.79188,70.39114],[72.5647,69.02085],[73.66787,68.4079],[73.2387,67.7404],[71.28,66.32],[72.42301,66.17267],[72.82077,66.53267],[73.92099,66.78946],[74.18651,67.28429],[75.052,67.76047],[74.46926,68.32899],[74.93584,68.98918],[73.84236,69.07146],[73.60187,69.62763],[74.3998,70.63175],[73.1011,71.44717],[74.89082,72.12119],[74.65926,72.83227],[75.15801,72.85497],[75.68351,72.30056],[75.28898,71.33556],[76.35911,71.15287],[75.90313,71.87401],[77.57665,72.26717],[79.65202,72.32011],[81.5,71.75],[80.61071,72.58285],[80.51109,73.6482],[82.25,73.85],[84.65526,73.80591],[86.8223,73.93688],[86.00956,74.45967],[87.16682,75.11643],[88.31571,75.14393],[90.26,75.64],[92.90058,75.77333],[93.23421,76.0472],[95.86,76.14],[96.67821,75.91548],[98.92254,76.44689],[100.75967,76.43028],[101.03532,76.86189],[101.99084,77.28754],[104.3516,77.69792],[106.06664,77.37389],[104.705,77.1274],[106.97013,76.97419]]],[[[105.07547,78.30689],[99.43814,77.921],[101.2649,79.23399],[102.08635,79.34641],[102.837815,79.28129],[105.37243,78.71334],[105.07547,78.30689]]],[[[51.136187,80.54728],[49.793685,80.415428],[48.894411,80.339567],[48.754937,80.175468],[47.586119,80.010181],[46.502826,80.247247],[47.072455,80.559424],[44.846958,80.58981],[46.799139,80.771918],[48.318477,80.78401],[48.522806,80.514569],[49.09719,80.753986],[50.039768,80.918885],[51.522933,80.699726],[51.136187,80.54728]]],[[[99.93976,78.88094],[97.75794,78.7562],[94.97259,79.044745],[93.31288,79.4265],[92.5454,80.14379],[91.18107,80.34146],[93.77766,81.0246],[95.940895,81.2504],[97.88385,80.746975],[100.186655,79.780135],[99.93976,78.88094]]]]},"type":"Feature","id":"RUS","properties":{"name":"Russia","id":"RUS","centroid":[37.35,55.45]}},{"geometry":{"type":"Polygon","coordinates":[[[30.419105,-1.134659],[30.816135,-1.698914],[30.758309,-2.28725],[30.469696,-2.413858],[29.938359,-2.348487],[29.632176,-2.917858],[29.024926,-2.839258],[29.117479,-2.292211],[29.254835,-2.21511],[29.291887,-1.620056],[29.579466,-1.341313],[29.821519,-1.443322],[30.419105,-1.134659]]]},"type":"Feature","id":"RWA","properties":{"name":"Rwanda","id":"RWA","centroid":[30.04,-1.59]}},{"geometry":{"type":"Polygon","coordinates":[[[-8.794884,27.120696],[-8.817828,27.656426],[-8.66559,27.656426],[-8.665124,27.589479],[-8.6844,27.395744],[-8.687294,25.881056],[-11.969419,25.933353],[-11.937224,23.374594],[-12.874222,23.284832],[-13.118754,22.77122],[-12.929102,21.327071],[-16.845194,21.333323],[-17.063423,20.999752],[-17.020428,21.42231],[-17.002962,21.420734],[-14.750955,21.5006],[-14.630833,21.86094],[-14.221168,22.310163],[-13.89111,23.691009],[-12.500963,24.770116],[-12.030759,26.030866],[-11.71822,26.104092],[-11.392555,26.883424],[-10.551263,26.990808],[-10.189424,26.860945],[-9.735343,26.860945],[-9.413037,27.088476],[-8.794884,27.120696]]]},"type":"Feature","id":"ESH","properties":{"name":"Western Sahara","id":"ESH","centroid":[-12.12,27.9]}},{"geometry":{"type":"Polygon","coordinates":[[[42.779332,16.347891],[42.649573,16.774635],[42.347989,17.075806],[42.270888,17.474722],[41.754382,17.833046],[41.221391,18.6716],[40.939341,19.486485],[40.247652,20.174635],[39.801685,20.338862],[39.139399,21.291905],[39.023696,21.986875],[39.066329,22.579656],[38.492772,23.688451],[38.02386,24.078686],[37.483635,24.285495],[37.154818,24.858483],[37.209491,25.084542],[36.931627,25.602959],[36.639604,25.826228],[36.249137,26.570136],[35.640182,27.37652],[35.130187,28.063352],[34.632336,28.058546],[34.787779,28.607427],[34.83222,28.957483],[34.956037,29.356555],[36.068941,29.197495],[36.501214,29.505254],[36.740528,29.865283],[37.503582,30.003776],[37.66812,30.338665],[37.998849,30.5085],[37.002166,31.508413],[39.004886,32.010217],[39.195468,32.161009],[40.399994,31.889992],[41.889981,31.190009],[44.709499,29.178891],[46.568713,29.099025],[47.459822,29.002519],[47.708851,28.526063],[48.416094,28.552004],[48.807595,27.689628],[49.299554,27.461218],[49.470914,27.109999],[50.152422,26.689663],[50.212935,26.277027],[50.113303,25.943972],[50.239859,25.60805],[50.527387,25.327808],[50.660557,24.999896],[50.810108,24.754743],[51.112415,24.556331],[51.389608,24.627386],[51.579519,24.245497],[51.617708,24.014219],[52.000733,23.001154],[55.006803,22.496948],[55.208341,22.70833],[55.666659,22.000001],[54.999982,19.999994],[52.00001,19.000003],[49.116672,18.616668],[48.183344,18.166669],[47.466695,17.116682],[47.000005,16.949999],[46.749994,17.283338],[46.366659,17.233315],[45.399999,17.333335],[45.216651,17.433329],[44.062613,17.410359],[43.791519,17.319977],[43.380794,17.579987],[43.115798,17.08844],[43.218375,16.66689],[42.779332,16.347891]]]},"type":"Feature","id":"SAU","properties":{"name":"Saudi Arabia","id":"SAU","centroid":[46.42,24.41]}},{"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.824963,9.484061],[33.842131,9.981915],[33.721959,10.325262],[33.206938,10.720112],[33.086766,11.441141],[33.206938,12.179338],[32.743419,12.248008],[32.67475,12.024832],[32.073892,11.97333],[32.314235,11.681484],[32.400072,11.080626],[31.850716,10.531271],[31.352862,9.810241],[30.837841,9.707237],[29.996639,10.290927],[29.618957,10.084919],[29.515953,9.793074],[29.000932,9.604232],[28.966597,9.398224],[27.97089,9.398224],[27.833551,9.604232],[27.112521,9.638567],[26.752006,9.466893],[26.477328,9.55273],[25.962307,10.136421],[25.790633,10.411099],[25.069604,10.27376],[24.794926,9.810241],[24.537415,8.917538],[24.194068,8.728696],[23.88698,8.61973],[23.805813,8.666319],[23.459013,8.954286],[23.394779,9.265068],[23.55725,9.681218],[23.554304,10.089255],[22.977544,10.714463],[22.864165,11.142395],[22.87622,11.38461],[22.50869,11.67936],[22.49762,12.26024],[22.28801,12.64605],[21.93681,12.58818],[22.03759,12.95546],[22.29658,13.37232],[22.18329,13.78648],[22.51202,14.09318],[22.30351,14.32682],[22.56795,14.94429],[23.02459,15.68072],[23.88689,15.61084],[23.83766,19.58047],[23.85,20],[25,20.00304],[25,22],[29.02,22],[32.9,22],[36.86623,22],[37.18872,21.01885],[36.96941,20.83744],[37.1147,19.80796],[37.48179,18.61409],[37.86276,18.36786],[38.41009,17.998307],[37.904,17.42754],[37.16747,17.26314],[36.85253,16.95655],[36.75389,16.29186],[36.32322,14.82249],[36.42951,14.42211],[36.27022,13.56333],[35.86363,12.57828],[35.26049,12.08286],[34.83163,11.31896],[34.73115,10.91017],[34.25745,10.63009],[33.96162,9.58358],[33.963393,9.464285]]]},"type":"Feature","id":"SDN","properties":{"name":"Sudan","id":"SDN","centroid":[32.31,15.36]}},{"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.97498,8.68456],[33.8255,8.37916],[33.2948,8.35458],[32.95418,7.78497],[33.56829,7.71334],[34.0751,7.22595],[34.25032,6.82607],[34.70702,6.59422],[35.298007,5.506],[34.620196,4.847123],[34.005,4.249885],[33.39,3.79],[32.68642,3.79232],[31.88145,3.55827],[31.24556,3.7819],[30.83385,3.50917],[29.95349,4.1737],[29.715995,4.600805],[29.159078,4.389267],[28.696678,4.455077],[28.428994,4.287155],[27.979977,4.408413],[27.374226,5.233944],[27.213409,5.550953],[26.465909,5.946717],[26.213418,6.546603],[25.796648,6.979316],[25.124131,7.500085],[25.114932,7.825104],[24.567369,8.229188],[23.88698,8.61973],[24.194068,8.728696],[24.537415,8.917538],[24.794926,9.810241],[25.069604,10.27376],[25.790633,10.411099],[25.962307,10.136421],[26.477328,9.55273],[26.752006,9.466893],[27.112521,9.638567],[27.833551,9.604232],[27.97089,9.398224],[28.966597,9.398224],[29.000932,9.604232],[29.515953,9.793074],[29.618957,10.084919],[29.996639,10.290927],[30.837841,9.707237],[31.352862,9.810241],[31.850716,10.531271],[32.400072,11.080626],[32.314235,11.681484],[32.073892,11.97333],[32.67475,12.024832],[32.743419,12.248008],[33.206938,12.179338],[33.086766,11.441141],[33.206938,10.720112],[33.721959,10.325262],[33.842131,9.981915],[33.824963,9.484061],[33.963393,9.464285]]]},"type":"Feature","id":"SSD","properties":{"name":"South Sudan","id":"SSD","centroid":[31.36,4.51]}},{"geometry":{"type":"Polygon","coordinates":[[[-16.713729,13.594959],[-17.126107,14.373516],[-17.625043,14.729541],[-17.185173,14.919477],[-16.700706,15.621527],[-16.463098,16.135036],[-16.12069,16.455663],[-15.623666,16.369337],[-15.135737,16.587282],[-14.577348,16.598264],[-14.099521,16.304302],[-13.435738,16.039383],[-12.830658,15.303692],[-12.17075,14.616834],[-12.124887,13.994727],[-11.927716,13.422075],[-11.553398,13.141214],[-11.467899,12.754519],[-11.513943,12.442988],[-11.658301,12.386583],[-12.203565,12.465648],[-12.278599,12.35444],[-12.499051,12.33209],[-13.217818,12.575874],[-13.700476,12.586183],[-15.548477,12.62817],[-15.816574,12.515567],[-16.147717,12.547762],[-16.677452,12.384852],[-16.841525,13.151394],[-15.931296,13.130284],[-15.691001,13.270353],[-15.511813,13.27857],[-15.141163,13.509512],[-14.712197,13.298207],[-14.277702,13.280585],[-13.844963,13.505042],[-14.046992,13.794068],[-14.376714,13.62568],[-14.687031,13.630357],[-15.081735,13.876492],[-15.39877,13.860369],[-15.624596,13.623587],[-16.713729,13.594959]]]},"type":"Feature","id":"SEN","properties":{"name":"Senegal","id":"SEN","centroid":[-17.29,14.34]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[162.119025,-10.482719],[162.398646,-10.826367],[161.700032,-10.820011],[161.319797,-10.204751],[161.917383,-10.446701],[162.119025,-10.482719]]],[[[160.852229,-9.872937],[160.462588,-9.89521],[159.849447,-9.794027],[159.640003,-9.63998],[159.702945,-9.24295],[160.362956,-9.400304],[160.688518,-9.610162],[160.852229,-9.872937]]],[[[161.679982,-9.599982],[161.529397,-9.784312],[160.788253,-8.917543],[160.579997,-8.320009],[160.920028,-8.320009],[161.280006,-9.120011],[161.679982,-9.599982]]],[[[159.875027,-8.33732],[159.917402,-8.53829],[159.133677,-8.114181],[158.586114,-7.754824],[158.21115,-7.421872],[158.359978,-7.320018],[158.820001,-7.560003],[159.640003,-8.020027],[159.875027,-8.33732]]],[[[157.538426,-7.34782],[157.33942,-7.404767],[156.90203,-7.176874],[156.491358,-6.765943],[156.542828,-6.599338],[157.14,-7.021638],[157.538426,-7.34782]]]]},"type":"Feature","id":"SLB","properties":{"name":"Solomon Islands","id":"SLB","centroid":[159.57,-9.27]}},{"geometry":{"type":"Polygon","coordinates":[[[-11.438779,6.785917],[-11.708195,6.860098],[-12.428099,7.262942],[-12.949049,7.798646],[-13.124025,8.163946],[-13.24655,8.903049],[-12.711958,9.342712],[-12.596719,9.620188],[-12.425929,9.835834],[-12.150338,9.858572],[-11.917277,10.046984],[-11.117481,10.045873],[-10.839152,9.688246],[-10.622395,9.26791],[-10.65477,8.977178],[-10.494315,8.715541],[-10.505477,8.348896],[-10.230094,8.406206],[-10.695595,7.939464],[-11.146704,7.396706],[-11.199802,7.105846],[-11.438779,6.785917]]]},"type":"Feature","id":"SLE","properties":{"name":"Sierra Leone","id":"SLE","centroid":[-13.17,8.3]}},{"geometry":{"type":"Polygon","coordinates":[[[-87.793111,13.38448],[-87.904112,13.149017],[-88.483302,13.163951],[-88.843228,13.259734],[-89.256743,13.458533],[-89.812394,13.520622],[-90.095555,13.735338],[-90.064678,13.88197],[-89.721934,14.134228],[-89.534219,14.244816],[-89.587343,14.362586],[-89.353326,14.424133],[-89.058512,14.340029],[-88.843073,14.140507],[-88.541231,13.980155],[-88.503998,13.845486],[-88.065343,13.964626],[-87.859515,13.893312],[-87.723503,13.78505],[-87.793111,13.38448]]]},"type":"Feature","id":"SLV","properties":{"name":"El Salvador","id":"SLV","centroid":[-89.1,13.4]}},{"geometry":{"type":"Polygon","coordinates":[[[49.72862,11.5789],[50.25878,11.67957],[50.73202,12.0219],[51.1112,12.02464],[51.13387,11.74815],[51.04153,11.16651],[51.04531,10.6409],[50.83418,10.27972],[50.55239,9.19874],[50.07092,8.08173],[49.4527,6.80466],[48.59455,5.33911],[47.74079,4.2194],[46.56476,2.85529],[45.56399,2.04576],[44.06815,1.05283],[43.13597,0.2922],[42.04157,-0.91916],[41.81095,-1.44647],[41.58513,-1.68325],[40.993,-0.85829],[40.98105,2.78452],[41.855083,3.918912],[42.12861,4.23413],[42.76967,4.25259],[43.66087,4.95755],[44.9636,5.00162],[47.78942,8.003],[46.948328,7.996877],[43.67875,9.18358],[43.296975,9.540477],[42.92812,10.02194],[42.55876,10.57258],[42.776852,10.926879],[43.145305,11.46204],[43.47066,11.27771],[43.666668,10.864169],[44.117804,10.445538],[44.614259,10.442205],[45.556941,10.698029],[46.645401,10.816549],[47.525658,11.127228],[48.021596,11.193064],[48.378784,11.375482],[48.948206,11.410622],[48.942005,11.394266],[48.948205,11.410617],[49.26776,11.43033],[49.72862,11.5789]]]},"type":"Feature","id":"SOM","properties":{"name":"Somalia","id":"SOM","centroid":[45.25,2.02]}},{"geometry":{"type":"Polygon","coordinates":[[[20.874313,45.416375],[21.483526,45.18117],[21.562023,44.768947],[22.145088,44.478422],[22.459022,44.702517],[22.705726,44.578003],[22.474008,44.409228],[22.65715,44.234923],[22.410446,44.008063],[22.500157,43.642814],[22.986019,43.211161],[22.604801,42.898519],[22.436595,42.580321],[22.545012,42.461362],[22.380526,42.32026],[21.91708,42.30364],[21.576636,42.245224],[21.3527,42.2068],[20.76216,42.05186],[20.76216,42.05186],[20.71731,41.84711],[20.59023,41.85541],[20.52295,42.21787],[20.28374,42.32025],[20.0707,42.58863],[20.25758,42.81275],[20.3398,42.89852],[19.95857,43.10604],[19.63,43.21378],[19.48389,43.35229],[19.21852,43.52384],[19.454,43.5681],[19.59976,44.03847],[19.11761,44.42307],[19.36803,44.863],[19.00548,44.86023],[19.390476,45.236516],[19.072769,45.521511],[18.82982,45.90888],[19.596045,46.17173],[20.220192,46.127469],[20.762175,45.734573],[20.874313,45.416375]]]},"type":"Feature","id":"SRB","properties":{"name":"Republic of Serbia","id":"SRB","centroid":[20.28,44.49]}},{"geometry":{"type":"Polygon","coordinates":[[[-57.147436,5.97315],[-55.949318,5.772878],[-55.84178,5.953125],[-55.03325,6.025291],[-53.958045,5.756548],[-54.478633,4.896756],[-54.399542,4.212611],[-54.006931,3.620038],[-54.181726,3.18978],[-54.269705,2.732392],[-54.524754,2.311849],[-55.097587,2.523748],[-55.569755,2.421506],[-55.973322,2.510364],[-56.073342,2.220795],[-55.9056,2.021996],[-55.995698,1.817667],[-56.539386,1.899523],[-57.150098,2.768927],[-57.281433,3.333492],[-57.601569,3.334655],[-58.044694,4.060864],[-57.86021,4.576801],[-57.914289,4.812626],[-57.307246,5.073567],[-57.147436,5.97315]]]},"type":"Feature","id":"SUR","properties":{"name":"Suriname","id":"SUR","centroid":[-55.1,5.5]}},{"geometry":{"type":"Polygon","coordinates":[[[18.853144,49.49623],[18.909575,49.435846],[19.320713,49.571574],[19.825023,49.217125],[20.415839,49.431453],[20.887955,49.328772],[21.607808,49.470107],[22.558138,49.085738],[22.280842,48.825392],[22.085608,48.422264],[21.872236,48.319971],[20.801294,48.623854],[20.473562,48.56285],[20.239054,48.327567],[19.769471,48.202691],[19.661364,48.266615],[19.174365,48.111379],[18.777025,48.081768],[18.696513,47.880954],[17.857133,47.758429],[17.488473,47.867466],[16.979667,48.123497],[16.879983,48.470013],[16.960288,48.596982],[17.101985,48.816969],[17.545007,48.800019],[17.886485,48.903475],[17.913512,48.996493],[18.104973,49.043983],[18.170498,49.271515],[18.399994,49.315001],[18.554971,49.495015],[18.853144,49.49623]]]},"type":"Feature","id":"SVK","properties":{"name":"Slovakia","id":"SVK","centroid":[17.07,48.1]}},{"geometry":{"type":"Polygon","coordinates":[[[13.806475,46.509306],[14.632472,46.431817],[15.137092,46.658703],[16.011664,46.683611],[16.202298,46.852386],[16.370505,46.841327],[16.564808,46.503751],[15.768733,46.238108],[15.67153,45.834154],[15.323954,45.731783],[15.327675,45.452316],[14.935244,45.471695],[14.595109,45.634941],[14.411968,45.466166],[13.71506,45.500324],[13.93763,45.591016],[13.69811,46.016778],[13.806475,46.509306]]]},"type":"Feature","id":"SVN","properties":{"name":"Slovenia","id":"SVN","centroid":[14.33,46.04]}},{"geometry":{"type":"Polygon","coordinates":[[[22.183173,65.723741],[21.213517,65.026005],[21.369631,64.413588],[19.778876,63.609554],[17.847779,62.7494],[17.119555,61.341166],[17.831346,60.636583],[18.787722,60.081914],[17.869225,58.953766],[16.829185,58.719827],[16.44771,57.041118],[15.879786,56.104302],[14.666681,56.200885],[14.100721,55.407781],[12.942911,55.361737],[12.625101,56.30708],[11.787942,57.441817],[11.027369,58.856149],[11.468272,59.432393],[12.300366,60.117933],[12.631147,61.293572],[11.992064,61.800362],[11.930569,63.128318],[12.579935,64.066219],[13.571916,64.049114],[13.919905,64.445421],[13.55569,64.787028],[15.108411,66.193867],[16.108712,67.302456],[16.768879,68.013937],[17.729182,68.010552],[17.993868,68.567391],[19.87856,68.407194],[20.025269,69.065139],[20.645593,69.106247],[21.978535,68.616846],[23.539473,67.936009],[23.56588,66.396051],[23.903379,66.006927],[22.183173,65.723741]]]},"type":"Feature","id":"SWE","properties":{"name":"Sweden","id":"SWE","centroid":[18.03,59.2]}},{"geometry":{"type":"Polygon","coordinates":[[[32.071665,-26.73382],[31.86806,-27.177927],[31.282773,-27.285879],[30.685962,-26.743845],[30.676609,-26.398078],[30.949667,-26.022649],[31.04408,-25.731452],[31.333158,-25.660191],[31.837778,-25.843332],[31.985779,-26.29178],[32.071665,-26.73382]]]},"type":"Feature","id":"SWZ","properties":{"name":"Swaziland","id":"SWZ","centroid":[31.06,-26.18]}},{"geometry":{"type":"Polygon","coordinates":[[[38.792341,33.378686],[36.834062,32.312938],[35.719918,32.709192],[35.700798,32.716014],[35.836397,32.868123],[35.821101,33.277426],[36.06646,33.824912],[36.61175,34.201789],[36.448194,34.593935],[35.998403,34.644914],[35.905023,35.410009],[36.149763,35.821535],[36.41755,36.040617],[36.685389,36.259699],[36.739494,36.81752],[37.066761,36.623036],[38.167727,36.90121],[38.699891,36.712927],[39.52258,36.716054],[40.673259,37.091276],[41.212089,37.074352],[42.349591,37.229873],[41.837064,36.605854],[41.289707,36.358815],[41.383965,35.628317],[41.006159,34.419372],[38.792341,33.378686]]]},"type":"Feature","id":"SYR","properties":{"name":"Syria","id":"SYR","centroid":[36.17,33.31]}},{"geometry":{"type":"Polygon","coordinates":[[[14.495787,12.859396],[14.595781,13.330427],[13.954477,13.353449],[13.956699,13.996691],[13.540394,14.367134],[13.97217,15.68437],[15.247731,16.627306],[15.300441,17.92795],[15.685741,19.95718],[15.903247,20.387619],[15.487148,20.730415],[15.47106,21.04845],[15.096888,21.308519],[14.8513,22.86295],[15.86085,23.40972],[19.84926,21.49509],[23.83766,19.58047],[23.88689,15.61084],[23.02459,15.68072],[22.56795,14.94429],[22.30351,14.32682],[22.51202,14.09318],[22.18329,13.78648],[22.29658,13.37232],[22.03759,12.95546],[21.93681,12.58818],[22.28801,12.64605],[22.49762,12.26024],[22.50869,11.67936],[22.87622,11.38461],[22.864165,11.142395],[22.231129,10.971889],[21.723822,10.567056],[21.000868,9.475985],[20.059685,9.012706],[19.094008,9.074847],[18.81201,8.982915],[18.911022,8.630895],[18.389555,8.281304],[17.96493,7.890914],[16.705988,7.508328],[16.456185,7.734774],[16.290562,7.754307],[16.106232,7.497088],[15.27946,7.421925],[15.436092,7.692812],[15.120866,8.38215],[14.979996,8.796104],[14.544467,8.965861],[13.954218,9.549495],[14.171466,10.021378],[14.627201,9.920919],[14.909354,9.992129],[15.467873,9.982337],[14.923565,10.891325],[14.960152,11.555574],[14.89336,12.21905],[14.495787,12.859396]]]},"type":"Feature","id":"TCD","properties":{"name":"Chad","id":"TCD","centroid":[14.59,12.1]}},{"geometry":{"type":"Polygon","coordinates":[[[1.865241,6.142158],[1.060122,5.928837],[0.836931,6.279979],[0.570384,6.914359],[0.490957,7.411744],[0.712029,8.312465],[0.461192,8.677223],[0.365901,9.465004],[0.36758,10.191213],[-0.049785,10.706918],[0.023803,11.018682],[0.899563,10.997339],[0.772336,10.470808],[1.077795,10.175607],[1.425061,9.825395],[1.463043,9.334624],[1.664478,9.12859],[1.618951,6.832038],[1.865241,6.142158]]]},"type":"Feature","id":"TGO","properties":{"name":"Togo","id":"TGO","centroid":[1.2,6.09]}},{"geometry":{"type":"Polygon","coordinates":[[[102.584932,12.186595],[101.687158,12.64574],[100.83181,12.627085],[100.978467,13.412722],[100.097797,13.406856],[100.018733,12.307001],[99.478921,10.846367],[99.153772,9.963061],[99.222399,9.239255],[99.873832,9.207862],[100.279647,8.295153],[100.459274,7.429573],[101.017328,6.856869],[101.623079,6.740622],[102.141187,6.221636],[101.814282,5.810808],[101.154219,5.691384],[101.075516,6.204867],[100.259596,6.642825],[100.085757,6.464489],[99.690691,6.848213],[99.519642,7.343454],[98.988253,7.907993],[98.503786,8.382305],[98.339662,7.794512],[98.150009,8.350007],[98.25915,8.973923],[98.553551,9.93296],[99.038121,10.960546],[99.587286,11.892763],[99.196354,12.804748],[99.212012,13.269294],[99.097755,13.827503],[98.430819,14.622028],[98.192074,15.123703],[98.537376,15.308497],[98.903348,16.177824],[98.493761,16.837836],[97.859123,17.567946],[97.375896,18.445438],[97.797783,18.62708],[98.253724,19.708203],[98.959676,19.752981],[99.543309,20.186598],[100.115988,20.41785],[100.548881,20.109238],[100.606294,19.508344],[101.282015,19.462585],[101.035931,18.408928],[101.059548,17.512497],[102.113592,18.109102],[102.413005,17.932782],[102.998706,17.961695],[103.200192,18.309632],[103.956477,18.240954],[104.716947,17.428859],[104.779321,16.441865],[105.589039,15.570316],[105.544338,14.723934],[105.218777,14.273212],[104.281418,14.416743],[102.988422,14.225721],[102.348099,13.394247],[102.584932,12.186595]]]},"type":"Feature","id":"THA","properties":{"name":"Thailand","id":"THA","centroid":[100.35,13.45]}},{"geometry":{"type":"Polygon","coordinates":[[[71.014198,40.244366],[70.648019,39.935754],[69.55961,40.103211],[69.464887,39.526683],[70.549162,39.604198],[71.784694,39.279463],[73.675379,39.431237],[73.928852,38.505815],[74.257514,38.606507],[74.864816,38.378846],[74.829986,37.990007],[74.980002,37.41999],[73.948696,37.421566],[73.260056,37.495257],[72.63689,37.047558],[72.193041,36.948288],[71.844638,36.738171],[71.448693,37.065645],[71.541918,37.905774],[71.239404,37.953265],[71.348131,38.258905],[70.806821,38.486282],[70.376304,38.138396],[70.270574,37.735165],[70.116578,37.588223],[69.518785,37.608997],[69.196273,37.151144],[68.859446,37.344336],[68.135562,37.023115],[67.83,37.144994],[68.392033,38.157025],[68.176025,38.901553],[67.44222,39.140144],[67.701429,39.580478],[68.536416,39.533453],[69.011633,40.086158],[69.329495,40.727824],[70.666622,40.960213],[70.45816,40.496495],[70.601407,40.218527],[71.014198,40.244366]]]},"type":"Feature","id":"TJK","properties":{"name":"Tajikistan","id":"TJK","centroid":[68.48,38.33]}},{"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[61.123071,36.491597],[60.377638,36.527383],[59.234762,37.412988],[58.436154,37.522309],[57.330434,38.029229],[56.619366,38.121394],[56.180375,37.935127],[55.511578,37.964117],[54.800304,37.392421],[53.921598,37.198918],[53.735511,37.906136],[53.880929,38.952093],[53.101028,39.290574],[53.357808,39.975286],[52.693973,40.033629],[52.915251,40.876523],[53.858139,40.631034],[54.736845,40.951015],[54.008311,41.551211],[53.721713,42.123191],[52.91675,41.868117],[52.814689,41.135371],[52.50246,41.783316],[52.944293,42.116034],[54.079418,42.324109],[54.755345,42.043971],[55.455251,41.259859],[55.968191,41.308642],[57.096391,41.32231],[56.932215,41.826026],[57.78653,42.170553],[58.629011,42.751551],[59.976422,42.223082],[60.083341,41.425146],[60.465953,41.220327],[61.547179,41.26637],[61.882714,41.084857],[62.37426,40.053886],[63.518015,39.363257],[64.170223,38.892407],[65.215999,38.402695],[66.54615,37.974685],[66.518607,37.362784],[66.217385,37.39379],[65.745631,37.661164],[65.588948,37.305217],[64.746105,37.111818],[64.546479,36.312073],[63.982896,36.007957],[63.193538,35.857166],[62.984662,35.404041],[62.230651,35.270664],[61.210817,35.650072]]]},"type":"Feature","id":"TKM","properties":{"name":"Turkmenistan","id":"TKM","centroid":[57.5,38]}},{"geometry":{"type":"Polygon","coordinates":[[[124.968682,-8.89279],[125.086246,-8.656887],[125.947072,-8.432095],[126.644704,-8.398247],[126.957243,-8.273345],[127.335928,-8.397317],[126.967992,-8.668256],[125.925885,-9.106007],[125.08852,-9.393173],[125.07002,-9.089987],[124.968682,-8.89279]]]},"type":"Feature","id":"TLS","properties":{"name":"East Timor","id":"TLS","centroid":[125.34,-8.29]}},{"geometry":{"type":"Polygon","coordinates":[[[-61.68,10.76],[-61.105,10.89],[-60.895,10.855],[-60.935,10.11],[-61.77,10],[-61.95,10.09],[-61.66,10.365],[-61.68,10.76]]]},"type":"Feature","id":"TTO","properties":{"name":"Trinidad and Tobago","id":"TTO","centroid":[-61.31,10.4]}},{"geometry":{"type":"Polygon","coordinates":[[[9.48214,30.307556],[9.055603,32.102692],[8.439103,32.506285],[8.430473,32.748337],[7.612642,33.344115],[7.524482,34.097376],[8.140981,34.655146],[8.376368,35.479876],[8.217824,36.433177],[8.420964,36.946427],[9.509994,37.349994],[10.210002,37.230002],[10.18065,36.724038],[11.028867,37.092103],[11.100026,36.899996],[10.600005,36.41],[10.593287,35.947444],[10.939519,35.698984],[10.807847,34.833507],[10.149593,34.330773],[10.339659,33.785742],[10.856836,33.76874],[11.108501,33.293343],[11.488787,33.136996],[11.432253,32.368903],[10.94479,32.081815],[10.636901,31.761421],[9.950225,31.37607],[10.056575,30.961831],[9.970017,30.539325],[9.48214,30.307556]]]},"type":"Feature","id":"TUN","properties":{"name":"Tunisia","id":"TUN","centroid":[10.11,36.5]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[36.913127,41.335358],[38.347665,40.948586],[39.512607,41.102763],[40.373433,41.013673],[41.554084,41.535656],[42.619549,41.583173],[43.582746,41.092143],[43.752658,40.740201],[43.656436,40.253564],[44.400009,40.005],[44.79399,39.713003],[44.109225,39.428136],[44.421403,38.281281],[44.225756,37.971584],[44.772699,37.170445],[44.293452,37.001514],[43.942259,37.256228],[42.779126,37.385264],[42.349591,37.229873],[41.212089,37.074352],[40.673259,37.091276],[39.52258,36.716054],[38.699891,36.712927],[38.167727,36.90121],[37.066761,36.623036],[36.739494,36.81752],[36.685389,36.259699],[36.41755,36.040617],[36.149763,35.821535],[35.782085,36.274995],[36.160822,36.650606],[35.550936,36.565443],[34.714553,36.795532],[34.026895,36.21996],[32.509158,36.107564],[31.699595,36.644275],[30.621625,36.677865],[30.391096,36.262981],[29.699976,36.144357],[28.732903,36.676831],[27.641187,36.658822],[27.048768,37.653361],[26.318218,38.208133],[26.8047,38.98576],[26.170785,39.463612],[27.28002,40.420014],[28.819978,40.460011],[29.240004,41.219991],[31.145934,41.087622],[32.347979,41.736264],[33.513283,42.01896],[35.167704,42.040225],[36.913127,41.335358]]],[[[27.192377,40.690566],[26.358009,40.151994],[26.043351,40.617754],[26.056942,40.824123],[26.294602,40.936261],[26.604196,41.562115],[26.117042,41.826905],[27.135739,42.141485],[27.99672,42.007359],[28.115525,41.622886],[28.988443,41.299934],[28.806438,41.054962],[27.619017,40.999823],[27.192377,40.690566]]]]},"type":"Feature","id":"TUR","properties":{"name":"Turkey","id":"TUR","centroid":[32.54,39.57]}},{"geometry":{"type":"Polygon","coordinates":[[[33.903711,-0.95],[34.07262,-1.05982],[37.69869,-3.09699],[37.7669,-3.67712],[39.20222,-4.67677],[38.74054,-5.90895],[38.79977,-6.47566],[39.44,-6.84],[39.47,-7.1],[39.19469,-7.7039],[39.25203,-8.00781],[39.18652,-8.48551],[39.53574,-9.11237],[39.9496,-10.0984],[40.31659,-10.3171],[39.521,-10.89688],[38.427557,-11.285202],[37.82764,-11.26879],[37.47129,-11.56876],[36.775151,-11.594537],[36.514082,-11.720938],[35.312398,-11.439146],[34.559989,-11.52002],[34.28,-10.16],[33.940838,-9.693674],[33.73972,-9.41715],[32.759375,-9.230599],[32.191865,-8.930359],[31.556348,-8.762049],[31.157751,-8.594579],[30.74,-8.34],[30.2,-7.08],[29.62,-6.52],[29.419993,-5.939999],[29.519987,-5.419979],[29.339998,-4.499983],[29.753512,-4.452389],[30.11632,-4.09012],[30.50554,-3.56858],[30.75224,-3.35931],[30.74301,-3.03431],[30.52766,-2.80762],[30.46967,-2.41383],[30.758309,-2.28725],[30.816135,-1.698914],[30.419105,-1.134659],[30.76986,-1.01455],[31.86617,-1.02736],[33.903711,-0.95]]]},"type":"Feature","id":"TZA","properties":{"name":"United Republic of Tanzania","id":"TZA","centroid":[35.45,-6.08]}},{"geometry":{"type":"Polygon","coordinates":[[[31.86617,-1.02736],[30.76986,-1.01455],[30.419105,-1.134659],[29.821519,-1.443322],[29.579466,-1.341313],[29.587838,-0.587406],[29.8195,-0.2053],[29.875779,0.59738],[30.086154,1.062313],[30.468508,1.583805],[30.85267,1.849396],[31.174149,2.204465],[30.77332,2.33989],[30.83385,3.50917],[31.24556,3.7819],[31.88145,3.55827],[32.68642,3.79232],[33.39,3.79],[34.005,4.249885],[34.47913,3.5556],[34.59607,3.05374],[35.03599,1.90584],[34.6721,1.17694],[34.18,0.515],[33.893569,0.109814],[33.903711,-0.95],[31.86617,-1.02736]]]},"type":"Feature","id":"UGA","properties":{"name":"Uganda","id":"UGA","centroid":[32.3,0.2]}},{"geometry":{"type":"Polygon","coordinates":[[[31.785998,52.101678],[32.159412,52.061267],[32.412058,52.288695],[32.715761,52.238465],[33.7527,52.335075],[34.391731,51.768882],[34.141978,51.566413],[34.224816,51.255993],[35.022183,51.207572],[35.377924,50.773955],[35.356116,50.577197],[36.626168,50.225591],[37.39346,50.383953],[38.010631,49.915662],[38.594988,49.926462],[40.069058,49.601055],[40.080789,49.30743],[39.674664,48.783818],[39.895632,48.232405],[39.738278,47.898937],[38.770585,47.825608],[38.255112,47.5464],[38.223538,47.10219],[37.425137,47.022221],[36.759855,46.6987],[35.823685,46.645964],[34.962342,46.273197],[35.020788,45.651219],[35.510009,45.409993],[36.529998,45.46999],[36.334713,45.113216],[35.239999,44.939996],[33.882511,44.361479],[33.326421,44.564877],[33.546924,45.034771],[32.454174,45.327466],[32.630804,45.519186],[33.588162,45.851569],[33.298567,46.080598],[31.74414,46.333348],[31.675307,46.706245],[30.748749,46.5831],[30.377609,46.03241],[29.603289,45.293308],[29.149725,45.464925],[28.679779,45.304031],[28.233554,45.488283],[28.485269,45.596907],[28.659987,45.939987],[28.933717,46.25883],[28.862972,46.437889],[29.072107,46.517678],[29.170654,46.379262],[29.759972,46.349988],[30.024659,46.423937],[29.83821,46.525326],[29.908852,46.674361],[29.559674,46.928583],[29.415135,47.346645],[29.050868,47.510227],[29.122698,47.849095],[28.670891,48.118149],[28.259547,48.155562],[27.522537,48.467119],[26.857824,48.368211],[26.619337,48.220726],[26.19745,48.220881],[25.945941,47.987149],[25.207743,47.891056],[24.866317,47.737526],[24.402056,47.981878],[23.760958,47.985598],[23.142236,48.096341],[22.710531,47.882194],[22.64082,48.15024],[22.085608,48.422264],[22.280842,48.825392],[22.558138,49.085738],[22.776419,49.027395],[22.51845,49.476774],[23.426508,50.308506],[23.922757,50.424881],[24.029986,50.705407],[23.527071,51.578454],[24.005078,51.617444],[24.553106,51.888461],[25.327788,51.910656],[26.337959,51.832289],[27.454066,51.592303],[28.241615,51.572227],[28.617613,51.427714],[28.992835,51.602044],[29.254938,51.368234],[30.157364,51.416138],[30.555117,51.319503],[30.619454,51.822806],[30.927549,52.042353],[31.785998,52.101678]]]},"type":"Feature","id":"UKR","properties":{"name":"Ukraine","id":"UKR","centroid":[30.28,50.3]}},{"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.976026,-30.109686],[-55.973245,-30.883076],[-55.60151,-30.853879],[-54.572452,-31.494511],[-53.787952,-32.047243],[-53.209589,-32.727666],[-53.650544,-33.202004],[-53.373662,-33.768378],[-53.806426,-34.396815],[-54.935866,-34.952647],[-55.67409,-34.752659],[-56.215297,-34.859836],[-57.139685,-34.430456],[-57.817861,-34.462547],[-58.427074,-33.909454],[-58.349611,-33.263189],[-58.132648,-33.040567],[-58.14244,-32.044504],[-57.874937,-31.016556],[-57.625133,-30.216295]]]},"type":"Feature","id":"URY","properties":{"name":"Uruguay","id":"URY","centroid":[-56.11,-34.5]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347,19.70294],[-156.02368,19.81422],[-155.85008,19.97729],[-155.91907,20.17395],[-155.86108,20.26721],[-155.78505,20.2487],[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.45328],[-155.22217,19.23972],[-155.54211,19.08348]]],[[[-156.07926,20.64397],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71055,20.92676],[-156.61258,21.01249],[-156.25711,20.91745],[-155.99566,20.76404],[-156.07926,20.64397]]],[[[-156.75824,21.17684],[-156.78933,21.06873],[-157.32521,21.09777],[-157.25027,21.21958],[-156.75824,21.17684]]],[[[-157.65283,21.32217],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283,21.32217]]],[[[-159.34512,21.982],[-159.46372,21.88299],[-159.80051,22.06533],[-159.74877,22.1382],[-159.5962,22.23618],[-159.36569,22.21494],[-159.34512,21.982]]],[[[-94.81758,49.38905],[-94.64,48.84],[-94.32914,48.67074],[-93.63087,48.60926],[-92.61,48.45],[-91.64,48.14],[-90.83,48.27],[-89.6,48.01],[-89.272917,48.019808],[-88.378114,48.302918],[-87.439793,47.94],[-86.461991,47.553338],[-85.652363,47.220219],[-84.87608,46.900083],[-84.779238,46.637102],[-84.543749,46.538684],[-84.6049,46.4396],[-84.3367,46.40877],[-84.14212,46.512226],[-84.091851,46.275419],[-83.890765,46.116927],[-83.616131,46.116927],[-83.469551,45.994686],[-83.592851,45.816894],[-82.550925,45.347517],[-82.337763,44.44],[-82.137642,43.571088],[-82.43,42.98],[-82.9,42.43],[-83.12,42.08],[-83.142,41.975681],[-83.02981,41.832796],[-82.690089,41.675105],[-82.439278,41.675105],[-81.277747,42.209026],[-80.247448,42.3662],[-78.939362,42.863611],[-78.92,42.965],[-79.01,43.27],[-79.171674,43.466339],[-78.72028,43.625089],[-77.737885,43.629056],[-76.820034,43.628784],[-76.5,44.018459],[-76.375,44.09631],[-75.31821,44.81645],[-74.867,45.00048],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.255],[-71.08482,45.30524],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.905,47.185],[-68.23444,47.35486],[-67.79046,47.06636],[-67.79134,45.70281],[-67.13741,45.13753],[-66.96466,44.8097],[-68.03252,44.3252],[-69.06,43.98],[-70.11617,43.68405],[-70.645476,43.090238],[-70.81489,42.8653],[-70.825,42.335],[-70.495,41.805],[-70.08,41.78],[-70.185,42.145],[-69.88497,41.92283],[-69.96503,41.63717],[-70.64,41.475],[-71.12039,41.49445],[-71.86,41.32],[-72.295,41.27],[-72.87643,41.22065],[-73.71,40.931102],[-72.24126,41.11948],[-71.945,40.93],[-73.345,40.63],[-73.982,40.628],[-73.952325,40.75075],[-74.25671,40.47351],[-73.96244,40.42763],[-74.17838,39.70926],[-74.90604,38.93954],[-74.98041,39.1964],[-75.20002,39.24845],[-75.52805,39.4985],[-75.32,38.96],[-75.071835,38.782032],[-75.05673,38.40412],[-75.37747,38.01551],[-75.94023,37.21689],[-76.03127,37.2566],[-75.72205,37.93705],[-76.23287,38.319215],[-76.35,39.15],[-76.542725,38.717615],[-76.32933,38.08326],[-76.989998,38.239992],[-76.30162,37.917945],[-76.25874,36.9664],[-75.9718,36.89726],[-75.86804,36.55125],[-75.72749,35.55074],[-76.36318,34.80854],[-77.397635,34.51201],[-78.05496,33.92547],[-78.55435,33.86133],[-79.06067,33.49395],[-79.20357,33.15839],[-80.301325,32.509355],[-80.86498,32.0333],[-81.33629,31.44049],[-81.49042,30.72999],[-81.31371,30.03552],[-80.98,29.18],[-80.535585,28.47213],[-80.53,28.04],[-80.056539,26.88],[-80.088015,26.205765],[-80.13156,25.816775],[-80.38103,25.20616],[-80.68,25.08],[-81.17213,25.20126],[-81.33,25.64],[-81.71,25.87],[-82.24,26.73],[-82.70515,27.49504],[-82.85526,27.88624],[-82.65,28.55],[-82.93,29.1],[-83.70959,29.93656],[-84.1,30.09],[-85.10882,29.63615],[-85.28784,29.68612],[-85.7731,30.15261],[-86.4,30.4],[-87.53036,30.27433],[-88.41782,30.3849],[-89.18049,30.31598],[-89.593831,30.159994],[-89.413735,29.89419],[-89.43,29.48864],[-89.21767,29.29108],[-89.40823,29.15961],[-89.77928,29.30714],[-90.15463,29.11743],[-90.880225,29.148535],[-91.626785,29.677],[-92.49906,29.5523],[-93.22637,29.78375],[-93.84842,29.71363],[-94.69,29.48],[-95.60026,28.73863],[-96.59404,28.30748],[-97.14,27.83],[-97.37,27.38],[-97.38,26.69],[-97.33,26.21],[-97.14,25.87],[-97.53,25.84],[-98.24,26.06],[-99.02,26.37],[-99.3,26.84],[-99.52,27.54],[-100.11,28.11],[-100.45584,28.69612],[-100.9576,29.38071],[-101.6624,29.7793],[-102.48,29.76],[-103.11,28.97],[-103.94,29.27],[-104.45697,29.57196],[-104.70575,30.12173],[-105.03737,30.64402],[-105.63159,31.08383],[-106.1429,31.39995],[-106.50759,31.75452],[-108.24,31.754854],[-108.24194,31.34222],[-109.035,31.34194],[-111.02361,31.33472],[-113.30498,32.03914],[-114.815,32.52528],[-114.72139,32.72083],[-115.99135,32.61239],[-117.12776,32.53534],[-117.295938,33.046225],[-117.944,33.621236],[-118.410602,33.740909],[-118.519895,34.027782],[-119.081,34.078],[-119.438841,34.348477],[-120.36778,34.44711],[-120.62286,34.60855],[-120.74433,35.15686],[-121.71457,36.16153],[-122.54747,37.55176],[-122.51201,37.78339],[-122.95319,38.11371],[-123.7272,38.95166],[-123.86517,39.76699],[-124.39807,40.3132],[-124.17886,41.14202],[-124.2137,41.99964],[-124.53284,42.76599],[-124.14214,43.70838],[-124.020535,44.615895],[-123.89893,45.52341],[-124.079635,46.86475],[-124.39567,47.72017],[-124.68721,48.184433],[-124.566101,48.379715],[-123.12,48.04],[-122.58736,47.096],[-122.34,47.36],[-122.5,48.18],[-122.84,49],[-120,49],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05,49],[-107.05,49],[-104.04826,48.99986],[-100.65,49],[-97.22872,49.0007],[-95.15907,49],[-95.15609,49.38425],[-94.81758,49.38905]]],[[[-153.006314,57.115842],[-154.00509,56.734677],[-154.516403,56.992749],[-154.670993,57.461196],[-153.76278,57.816575],[-153.228729,57.968968],[-152.564791,57.901427],[-152.141147,57.591059],[-153.006314,57.115842]]],[[[-165.579164,59.909987],[-166.19277,59.754441],[-166.848337,59.941406],[-167.455277,60.213069],[-166.467792,60.38417],[-165.67443,60.293607],[-165.579164,59.909987]]],[[[-171.731657,63.782515],[-171.114434,63.592191],[-170.491112,63.694975],[-169.682505,63.431116],[-168.689439,63.297506],[-168.771941,63.188598],[-169.52944,62.976931],[-170.290556,63.194438],[-170.671386,63.375822],[-171.553063,63.317789],[-171.791111,63.405846],[-171.731657,63.782515]]],[[[-155.06779,71.147776],[-154.344165,70.696409],[-153.900006,70.889989],[-152.210006,70.829992],[-152.270002,70.600006],[-150.739992,70.430017],[-149.720003,70.53001],[-147.613362,70.214035],[-145.68999,70.12001],[-144.920011,69.989992],[-143.589446,70.152514],[-142.07251,69.851938],[-140.985988,69.711998],[-140.985988,69.711998],[-140.992499,66.000029],[-140.99777,60.306397],[-140.012998,60.276838],[-139.039,60.000007],[-138.34089,59.56211],[-137.4525,58.905],[-136.47972,59.46389],[-135.47583,59.78778],[-134.945,59.27056],[-134.27111,58.86111],[-133.355549,58.410285],[-132.73042,57.69289],[-131.70781,56.55212],[-130.00778,55.91583],[-129.979994,55.284998],[-130.53611,54.802753],[-131.085818,55.178906],[-131.967211,55.497776],[-132.250011,56.369996],[-133.539181,57.178887],[-134.078063,58.123068],[-135.038211,58.187715],[-136.628062,58.212209],[-137.800006,58.499995],[-139.867787,59.537762],[-140.825274,59.727517],[-142.574444,60.084447],[-143.958881,59.99918],[-145.925557,60.45861],[-147.114374,60.884656],[-148.224306,60.672989],[-148.018066,59.978329],[-148.570823,59.914173],[-149.727858,59.705658],[-150.608243,59.368211],[-151.716393,59.155821],[-151.859433,59.744984],[-151.409719,60.725803],[-150.346941,61.033588],[-150.621111,61.284425],[-151.895839,60.727198],[-152.57833,60.061657],[-154.019172,59.350279],[-153.287511,58.864728],[-154.232492,58.146374],[-155.307491,57.727795],[-156.308335,57.422774],[-156.556097,56.979985],[-158.117217,56.463608],[-158.433321,55.994154],[-159.603327,55.566686],[-160.28972,55.643581],[-161.223048,55.364735],[-162.237766,55.024187],[-163.069447,54.689737],[-164.785569,54.404173],[-164.942226,54.572225],[-163.84834,55.039431],[-162.870001,55.348043],[-161.804175,55.894986],[-160.563605,56.008055],[-160.07056,56.418055],[-158.684443,57.016675],[-158.461097,57.216921],[-157.72277,57.570001],[-157.550274,58.328326],[-157.041675,58.918885],[-158.194731,58.615802],[-158.517218,58.787781],[-159.058606,58.424186],[-159.711667,58.93139],[-159.981289,58.572549],[-160.355271,59.071123],[-161.355003,58.670838],[-161.968894,58.671665],[-162.054987,59.266925],[-161.874171,59.633621],[-162.518059,59.989724],[-163.818341,59.798056],[-164.662218,60.267484],[-165.346388,60.507496],[-165.350832,61.073895],[-166.121379,61.500019],[-165.734452,62.074997],[-164.919179,62.633076],[-164.562508,63.146378],[-163.753332,63.219449],[-163.067224,63.059459],[-162.260555,63.541936],[-161.53445,63.455817],[-160.772507,63.766108],[-160.958335,64.222799],[-161.518068,64.402788],[-160.777778,64.788604],[-161.391926,64.777235],[-162.45305,64.559445],[-162.757786,64.338605],[-163.546394,64.55916],[-164.96083,64.446945],[-166.425288,64.686672],[-166.845004,65.088896],[-168.11056,65.669997],[-166.705271,66.088318],[-164.47471,66.57666],[-163.652512,66.57666],[-163.788602,66.077207],[-161.677774,66.11612],[-162.489715,66.735565],[-163.719717,67.116395],[-164.430991,67.616338],[-165.390287,68.042772],[-166.764441,68.358877],[-166.204707,68.883031],[-164.430811,68.915535],[-163.168614,69.371115],[-162.930566,69.858062],[-161.908897,70.33333],[-160.934797,70.44769],[-159.039176,70.891642],[-158.119723,70.824721],[-156.580825,71.357764],[-155.06779,71.147776]]]]},"type":"Feature","id":"USA","properties":{"name":"United States of America","id":"USA","centroid":[-77.02,39.91]}},{"geometry":{"type":"Polygon","coordinates":[[[66.518607,37.362784],[66.54615,37.974685],[65.215999,38.402695],[64.170223,38.892407],[63.518015,39.363257],[62.37426,40.053886],[61.882714,41.084857],[61.547179,41.26637],[60.465953,41.220327],[60.083341,41.425146],[59.976422,42.223082],[58.629011,42.751551],[57.78653,42.170553],[56.932215,41.826026],[57.096391,41.32231],[55.968191,41.308642],[55.928917,44.995858],[58.503127,45.586804],[58.689989,45.500014],[60.239972,44.784037],[61.05832,44.405817],[62.0133,43.504477],[63.185787,43.650075],[64.900824,43.728081],[66.098012,42.99766],[66.023392,41.994646],[66.510649,41.987644],[66.714047,41.168444],[67.985856,41.135991],[68.259896,40.662325],[68.632483,40.668681],[69.070027,41.384244],[70.388965,42.081308],[70.962315,42.266154],[71.259248,42.167711],[70.420022,41.519998],[71.157859,41.143587],[71.870115,41.3929],[73.055417,40.866033],[71.774875,40.145844],[71.014198,40.244366],[70.601407,40.218527],[70.45816,40.496495],[70.666622,40.960213],[69.329495,40.727824],[69.011633,40.086158],[68.536416,39.533453],[67.701429,39.580478],[67.44222,39.140144],[68.176025,38.901553],[68.392033,38.157025],[67.83,37.144994],[67.075782,37.356144],[66.518607,37.362784]]]},"type":"Feature","id":"UZB","properties":{"name":"Uzbekistan","id":"UZB","centroid":[69.1,41.2]}},{"geometry":{"type":"Polygon","coordinates":[[[-71.331584,11.776284],[-71.360006,11.539994],[-71.94705,11.423282],[-71.620868,10.96946],[-71.633064,10.446494],[-72.074174,9.865651],[-71.695644,9.072263],[-71.264559,9.137195],[-71.039999,9.859993],[-71.350084,10.211935],[-71.400623,10.968969],[-70.155299,11.375482],[-70.293843,11.846822],[-69.943245,12.162307],[-69.5843,11.459611],[-68.882999,11.443385],[-68.233271,10.885744],[-68.194127,10.554653],[-67.296249,10.545868],[-66.227864,10.648627],[-65.655238,10.200799],[-64.890452,10.077215],[-64.329479,10.389599],[-64.318007,10.641418],[-63.079322,10.701724],[-61.880946,10.715625],[-62.730119,10.420269],[-62.388512,9.948204],[-61.588767,9.873067],[-60.830597,9.38134],[-60.671252,8.580174],[-60.150096,8.602757],[-59.758285,8.367035],[-60.550588,7.779603],[-60.637973,7.415],[-60.295668,7.043911],[-60.543999,6.856584],[-61.159336,6.696077],[-61.139415,6.234297],[-61.410303,5.959068],[-60.733574,5.200277],[-60.601179,4.918098],[-60.966893,4.536468],[-62.08543,4.162124],[-62.804533,4.006965],[-63.093198,3.770571],[-63.888343,4.02053],[-64.628659,4.148481],[-64.816064,4.056445],[-64.368494,3.79721],[-64.408828,3.126786],[-64.269999,2.497006],[-63.422867,2.411068],[-63.368788,2.2009],[-64.083085,1.916369],[-64.199306,1.492855],[-64.611012,1.328731],[-65.354713,1.095282],[-65.548267,0.789254],[-66.325765,0.724452],[-66.876326,1.253361],[-67.181294,2.250638],[-67.447092,2.600281],[-67.809938,2.820655],[-67.303173,3.318454],[-67.337564,3.542342],[-67.621836,3.839482],[-67.823012,4.503937],[-67.744697,5.221129],[-67.521532,5.55687],[-67.34144,6.095468],[-67.695087,6.267318],[-68.265052,6.153268],[-68.985319,6.206805],[-69.38948,6.099861],[-70.093313,6.960376],[-70.674234,7.087785],[-71.960176,6.991615],[-72.198352,7.340431],[-72.444487,7.423785],[-72.479679,7.632506],[-72.360901,8.002638],[-72.439862,8.405275],[-72.660495,8.625288],[-72.78873,9.085027],[-73.304952,9.152],[-73.027604,9.73677],[-72.905286,10.450344],[-72.614658,10.821975],[-72.227575,11.108702],[-71.973922,11.608672],[-71.331584,11.776284]]]},"type":"Feature","id":"VEN","properties":{"name":"Venezuela","id":"VEN","centroid":[-66.55,10.3]}},{"geometry":{"type":"Polygon","coordinates":[[[108.05018,21.55238],[106.715068,20.696851],[105.881682,19.75205],[105.662006,19.058165],[106.426817,18.004121],[107.361954,16.697457],[108.269495,16.079742],[108.877107,15.276691],[109.33527,13.426028],[109.200136,11.666859],[108.36613,11.008321],[107.220929,10.364484],[106.405113,9.53084],[105.158264,8.59976],[104.795185,9.241038],[105.076202,9.918491],[104.334335,10.486544],[105.199915,10.88931],[106.24967,10.961812],[105.810524,11.567615],[107.491403,12.337206],[107.614548,13.535531],[107.382727,14.202441],[107.564525,15.202173],[107.312706,15.908538],[106.556008,16.604284],[105.925762,17.485315],[105.094598,18.666975],[103.896532,19.265181],[104.183388,19.624668],[104.822574,19.886642],[104.435,20.758733],[103.203861,20.766562],[102.754896,21.675137],[102.170436,22.464753],[102.706992,22.708795],[103.504515,22.703757],[104.476858,22.81915],[105.329209,23.352063],[105.811247,22.976892],[106.725403,22.794268],[106.567273,22.218205],[107.04342,21.811899],[108.05018,21.55238]]]},"type":"Feature","id":"VNM","properties":{"name":"Vietnam","id":"VNM","centroid":[105.55,21.05]}},{"geometry":{"type":"MultiPolygon","coordinates":[[[[167.844877,-16.466333],[167.515181,-16.59785],[167.180008,-16.159995],[167.216801,-15.891846],[167.844877,-16.466333]]],[[[167.107712,-14.93392],[167.270028,-15.740021],[167.001207,-15.614602],[166.793158,-15.668811],[166.649859,-15.392704],[166.629137,-14.626497],[167.107712,-14.93392]]]]},"type":"Feature","id":"VUT","properties":{"name":"Vanuatu","id":"VUT","centroid":[168.18,-17.45]}},{"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.545252,31.782505],[35.397561,31.489086],[34.927408,31.353435],[34.970507,31.616778],[35.225892,31.754341],[34.974641,31.866582],[35.18393,32.532511],[35.545665,32.393992]]]},"type":"Feature","id":"PSE","properties":{"name":"West Bank","id":"PSE","centroid":[35.14,31.47]}},{"geometry":{"type":"Polygon","coordinates":[[[53.108573,16.651051],[52.385206,16.382411],[52.191729,15.938433],[52.168165,15.59742],[51.172515,15.17525],[49.574576,14.708767],[48.679231,14.003202],[48.238947,13.94809],[47.938914,14.007233],[47.354454,13.59222],[46.717076,13.399699],[45.877593,13.347764],[45.62505,13.290946],[45.406459,13.026905],[45.144356,12.953938],[44.989533,12.699587],[44.494576,12.721653],[44.175113,12.58595],[43.482959,12.6368],[43.222871,13.22095],[43.251448,13.767584],[43.087944,14.06263],[42.892245,14.802249],[42.604873,15.213335],[42.805015,15.261963],[42.702438,15.718886],[42.823671,15.911742],[42.779332,16.347891],[43.218375,16.66689],[43.115798,17.08844],[43.380794,17.579987],[43.791519,17.319977],[44.062613,17.410359],[45.216651,17.433329],[45.399999,17.333335],[46.366659,17.233315],[46.749994,17.283338],[47.000005,16.949999],[47.466695,17.116682],[48.183344,18.166669],[49.116672,18.616668],[52.00001,19.000003],[52.782184,17.349742],[53.108573,16.651051]]]},"type":"Feature","id":"YEM","properties":{"name":"Yemen","id":"YEM","centroid":[44.2,15.35]}},{"geometry":{"type":"Polygon","coordinates":[[[31.521001,-29.257387],[31.325561,-29.401978],[30.901763,-29.909957],[30.622813,-30.423776],[30.055716,-31.140269],[28.925553,-32.172041],[28.219756,-32.771953],[27.464608,-33.226964],[26.419452,-33.61495],[25.909664,-33.66704],[25.780628,-33.944646],[25.172862,-33.796851],[24.677853,-33.987176],[23.594043,-33.794474],[22.988189,-33.916431],[22.574157,-33.864083],[21.542799,-34.258839],[20.689053,-34.417175],[20.071261,-34.795137],[19.616405,-34.819166],[19.193278,-34.462599],[18.855315,-34.444306],[18.424643,-33.997873],[18.377411,-34.136521],[18.244499,-33.867752],[18.25008,-33.281431],[17.92519,-32.611291],[18.24791,-32.429131],[18.221762,-31.661633],[17.566918,-30.725721],[17.064416,-29.878641],[17.062918,-29.875954],[16.344977,-28.576705],[16.824017,-28.082162],[17.218929,-28.355943],[17.387497,-28.783514],[17.836152,-28.856378],[18.464899,-29.045462],[19.002127,-28.972443],[19.894734,-28.461105],[19.895768,-24.76779],[20.165726,-24.917962],[20.758609,-25.868136],[20.66647,-26.477453],[20.889609,-26.828543],[21.605896,-26.726534],[22.105969,-26.280256],[22.579532,-25.979448],[22.824271,-25.500459],[23.312097,-25.26869],[23.73357,-25.390129],[24.211267,-25.670216],[25.025171,-25.71967],[25.664666,-25.486816],[25.765849,-25.174845],[25.941652,-24.696373],[26.485753,-24.616327],[26.786407,-24.240691],[27.11941,-23.574323],[28.017236,-22.827754],[29.432188,-22.091313],[29.839037,-22.102216],[30.322883,-22.271612],[30.659865,-22.151567],[31.191409,-22.25151],[31.670398,-23.658969],[31.930589,-24.369417],[31.752408,-25.484284],[31.837778,-25.843332],[31.333158,-25.660191],[31.04408,-25.731452],[30.949667,-26.022649],[30.676609,-26.398078],[30.685962,-26.743845],[31.282773,-27.285879],[31.86806,-27.177927],[32.071665,-26.73382],[32.83012,-26.742192],[32.580265,-27.470158],[32.462133,-28.301011],[32.203389,-28.752405],[31.521001,-29.257387]],[[28.978263,-28.955597],[28.5417,-28.647502],[28.074338,-28.851469],[27.532511,-29.242711],[26.999262,-29.875954],[27.749397,-30.645106],[28.107205,-30.545732],[28.291069,-30.226217],[28.8484,-30.070051],[29.018415,-29.743766],[29.325166,-29.257387],[28.978263,-28.955597]]]},"type":"Feature","id":"ZAF","properties":{"name":"South Africa","id":"ZAF","centroid":[28.11,-25.44]}},{"geometry":{"type":"Polygon","coordinates":[[[32.759375,-9.230599],[33.231388,-9.676722],[33.485688,-10.525559],[33.31531,-10.79655],[33.114289,-11.607198],[33.306422,-12.435778],[32.991764,-12.783871],[32.688165,-13.712858],[33.214025,-13.97186],[30.179481,-14.796099],[30.274256,-15.507787],[29.516834,-15.644678],[28.947463,-16.043051],[28.825869,-16.389749],[28.467906,-16.4684],[27.598243,-17.290831],[27.044427,-17.938026],[26.706773,-17.961229],[26.381935,-17.846042],[25.264226,-17.73654],[25.084443,-17.661816],[25.07695,-17.578823],[24.682349,-17.353411],[24.033862,-17.295843],[23.215048,-17.523116],[22.562478,-16.898451],[21.887843,-16.08031],[21.933886,-12.898437],[24.016137,-12.911046],[23.930922,-12.565848],[24.079905,-12.191297],[23.904154,-11.722282],[24.017894,-11.237298],[23.912215,-10.926826],[24.257155,-10.951993],[24.314516,-11.262826],[24.78317,-11.238694],[25.418118,-11.330936],[25.75231,-11.784965],[26.553088,-11.92444],[27.16442,-11.608748],[27.388799,-12.132747],[28.155109,-12.272481],[28.523562,-12.698604],[28.934286,-13.248958],[29.699614,-13.257227],[29.616001,-12.178895],[29.341548,-12.360744],[28.642417,-11.971569],[28.372253,-11.793647],[28.49607,-10.789884],[28.673682,-9.605925],[28.449871,-9.164918],[28.734867,-8.526559],[29.002912,-8.407032],[30.346086,-8.238257],[30.740015,-8.340007],[31.157751,-8.594579],[31.556348,-8.762049],[32.191865,-8.930359],[32.759375,-9.230599]]]},"type":"Feature","id":"ZMB","properties":{"name":"Zambia","id":"ZMB","centroid":[28.16,-15.28]}},{"geometry":{"type":"Polygon","coordinates":[[[103.9697265625,1.331445312499994],[103.65019531249999,1.325537109374991],[103.81796875000003,1.447070312499989],[103.9697265625,1.331445312499994]]]},"type":"Feature","id":"SGP","properties":{"childNum":1,"name":"Singapore","id":"SGP","centroid":[103.51,1.18]}},{"geometry":{"type":"Polygon","coordinates":[[[31.191409,-22.25151],[30.659865,-22.151567],[30.322883,-22.271612],[29.839037,-22.102216],[29.432188,-22.091313],[28.794656,-21.639454],[28.02137,-21.485975],[27.727228,-20.851802],[27.724747,-20.499059],[27.296505,-20.39152],[26.164791,-19.293086],[25.850391,-18.714413],[25.649163,-18.536026],[25.264226,-17.73654],[26.381935,-17.846042],[26.706773,-17.961229],[27.044427,-17.938026],[27.598243,-17.290831],[28.467906,-16.4684],[28.825869,-16.389749],[28.947463,-16.043051],[29.516834,-15.644678],[30.274256,-15.507787],[30.338955,-15.880839],[31.173064,-15.860944],[31.636498,-16.07199],[31.852041,-16.319417],[32.328239,-16.392074],[32.847639,-16.713398],[32.849861,-17.979057],[32.654886,-18.67209],[32.611994,-19.419383],[32.772708,-19.715592],[32.659743,-20.30429],[32.508693,-20.395292],[32.244988,-21.116489],[31.191409,-22.25151]]]},"type":"Feature","id":"ZWE","properties":{"name":"Zimbabwe","id":"ZWE","centroid":[31.02,-17.43]}}],"offset":{"y":90,"x":170}} \ No newline at end of file diff --git a/passiflora-ui/src/components/breadcrumb/index.vue b/passiflora-ui/src/components/breadcrumb/index.vue new file mode 100644 index 0000000..e4c9f13 --- /dev/null +++ b/passiflora-ui/src/components/breadcrumb/index.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/passiflora-ui/src/components/chart/index.vue b/passiflora-ui/src/components/chart/index.vue new file mode 100644 index 0000000..417c4c8 --- /dev/null +++ b/passiflora-ui/src/components/chart/index.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/passiflora-ui/src/components/footer/index.vue b/passiflora-ui/src/components/footer/index.vue new file mode 100644 index 0000000..7bb1dd1 --- /dev/null +++ b/passiflora-ui/src/components/footer/index.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/passiflora-ui/src/components/global-setting/block.vue b/passiflora-ui/src/components/global-setting/block.vue new file mode 100644 index 0000000..7a2bc3b --- /dev/null +++ b/passiflora-ui/src/components/global-setting/block.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/passiflora-ui/src/components/global-setting/form-wrapper.vue b/passiflora-ui/src/components/global-setting/form-wrapper.vue new file mode 100644 index 0000000..fe3bbbe --- /dev/null +++ b/passiflora-ui/src/components/global-setting/form-wrapper.vue @@ -0,0 +1,39 @@ + + + diff --git a/passiflora-ui/src/components/global-setting/index.vue b/passiflora-ui/src/components/global-setting/index.vue new file mode 100644 index 0000000..a2ee1a4 --- /dev/null +++ b/passiflora-ui/src/components/global-setting/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/passiflora-ui/src/components/icon-select/index.tsx b/passiflora-ui/src/components/icon-select/index.tsx new file mode 100644 index 0000000..dc4e489 --- /dev/null +++ b/passiflora-ui/src/components/icon-select/index.tsx @@ -0,0 +1,70 @@ +import { defineComponent, PropType } from 'vue'; +import Icon from '@arco-design/web-vue/es/icon'; +import IconUi from '@/components/icon'; +import { Select } from '@arco-design/web-vue'; + +interface Option { + label: string; + value: string; +} + +interface Props { + disabled: boolean; + modelValue: string; +} + +export default defineComponent({ + name: 'IconSelect', + props: { + modelValue: { + type: String as PropType, + required: true, + }, + disabled: { + type: Boolean as PropType, + required: true, + }, + }, + emits: ['update:modelValue'], + setup(props: Props, { emit }) { + const handleChange = (value: string) => { + emit('update:modelValue', value); + }; + + const formattedIcons: Option[] = Object.keys(Icon) + .filter((item) => item.startsWith('Icon')) + .map((item) => item.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()) + .map((item) => ({ + label: item, + value: item, + })); + + return () => ( + + ); + }, +}); diff --git a/passiflora-ui/src/components/icon/index.tsx b/passiflora-ui/src/components/icon/index.tsx new file mode 100644 index 0000000..1a6906f --- /dev/null +++ b/passiflora-ui/src/components/icon/index.tsx @@ -0,0 +1,10 @@ +import { h, compile } from 'vue'; + +type Props = { + name: string; +}; +const icon = (props: Props) => { + return props.name ? h(compile(`<${props.name}/>`)) : null; +}; + +export default icon; diff --git a/passiflora-ui/src/components/index.ts b/passiflora-ui/src/components/index.ts new file mode 100644 index 0000000..f418972 --- /dev/null +++ b/passiflora-ui/src/components/index.ts @@ -0,0 +1,35 @@ +import { App } from 'vue'; +import { use } from 'echarts/core'; +import { CanvasRenderer } from 'echarts/renderers'; +import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts'; +import { + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent, +} from 'echarts/components'; +import Chart from './chart/index.vue'; +import Breadcrumb from './breadcrumb/index.vue'; + +// Manually introduce ECharts modules to reduce packing size + +use([ + CanvasRenderer, + BarChart, + LineChart, + PieChart, + RadarChart, + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent, +]); + +export default { + install(Vue: App) { + Vue.component('Chart', Chart); + Vue.component('Breadcrumb', Breadcrumb); + }, +}; diff --git a/passiflora-ui/src/components/menu/index.vue b/passiflora-ui/src/components/menu/index.vue new file mode 100644 index 0000000..f9c2462 --- /dev/null +++ b/passiflora-ui/src/components/menu/index.vue @@ -0,0 +1,174 @@ + + + diff --git a/passiflora-ui/src/components/menu/use-menu-tree.ts b/passiflora-ui/src/components/menu/use-menu-tree.ts new file mode 100644 index 0000000..dfc5a59 --- /dev/null +++ b/passiflora-ui/src/components/menu/use-menu-tree.ts @@ -0,0 +1,73 @@ +import { computed } from 'vue'; +import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router'; +import usePermission from '@/hooks/permission'; +import { useAppStore } from '@/store'; +import appClientMenus from '@/router/app-menus'; +import { cloneDeep } from 'lodash'; + +export default function useMenuTree() { + const permission = usePermission(); + const appStore = useAppStore(); + const appRoute = computed(() => { + if (appStore.menuFromServer) { + return appStore.appAsyncMenus; + } + return appClientMenus; + }); + const menuTree = computed(() => { + const copyRouter = cloneDeep(appRoute.value) as RouteRecordNormalized[]; + copyRouter.sort((a: RouteRecordNormalized, b: RouteRecordNormalized) => { + return (a.meta.order || 0) - (b.meta.order || 0); + }); + function travel(_routes: RouteRecordRaw[], layer: number) { + if (!_routes) return null; + + const collector: any = _routes.map((element) => { + // no access + if (!permission.accessRouter(element)) { + return null; + } + + // leaf node + if ( + element.meta?.hideChildrenInMenu || + !element.children || + !element.children.length + ) { + element.children = []; + return element; + } + + // route filter hideInMenu true + element.children = element.children.filter( + (x) => x.meta?.hideInMenu !== true + ); + + // Associated child node + const subItem = travel(element.children, layer + 1); + + if (subItem.length) { + element.children = subItem; + return element; + } + // the else logic + if (layer > 1) { + element.children = subItem; + return element; + } + + if (element.meta?.hideInMenu === false) { + return element; + } + + return null; + }); + return collector.filter(Boolean); + } + return travel(copyRouter, 0); + }); + + return { + menuTree, + }; +} diff --git a/passiflora-ui/src/components/message-box/index.vue b/passiflora-ui/src/components/message-box/index.vue new file mode 100644 index 0000000..8343821 --- /dev/null +++ b/passiflora-ui/src/components/message-box/index.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/passiflora-ui/src/components/message-box/list.vue b/passiflora-ui/src/components/message-box/list.vue new file mode 100644 index 0000000..4032ac1 --- /dev/null +++ b/passiflora-ui/src/components/message-box/list.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/passiflora-ui/src/components/message-box/locale/en-US.ts b/passiflora-ui/src/components/message-box/locale/en-US.ts new file mode 100644 index 0000000..0e1b0e1 --- /dev/null +++ b/passiflora-ui/src/components/message-box/locale/en-US.ts @@ -0,0 +1,12 @@ +export default { + 'messageBox.tab.title.message': 'Message', + 'messageBox.tab.title.notice': 'Notice', + 'messageBox.tab.title.todo': 'Todo', + 'messageBox.tab.button': 'empty', + 'messageBox.allRead': 'All Read', + 'messageBox.viewMore': 'View More', + 'messageBox.noContent': 'No Content', + 'messageBox.userCenter': 'User Center', + 'messageBox.userSettings': 'User Settings', + 'messageBox.logout': 'Logout', +}; diff --git a/passiflora-ui/src/components/message-box/locale/zh-CN.ts b/passiflora-ui/src/components/message-box/locale/zh-CN.ts new file mode 100644 index 0000000..a1cf1a5 --- /dev/null +++ b/passiflora-ui/src/components/message-box/locale/zh-CN.ts @@ -0,0 +1,12 @@ +export default { + 'messageBox.tab.title.message': '消息', + 'messageBox.tab.title.notice': '通知', + 'messageBox.tab.title.todo': '待办', + 'messageBox.tab.button': '清空', + 'messageBox.allRead': '全部已读', + 'messageBox.viewMore': '查看更多', + 'messageBox.noContent': '暂无内容', + 'messageBox.userCenter': '用户中心', + 'messageBox.userSettings': '用户设置', + 'messageBox.logout': '登出登录', +}; diff --git a/passiflora-ui/src/components/navbar/index.vue b/passiflora-ui/src/components/navbar/index.vue new file mode 100644 index 0000000..fa27cb0 --- /dev/null +++ b/passiflora-ui/src/components/navbar/index.vue @@ -0,0 +1,301 @@ + + + + + + + diff --git a/passiflora-ui/src/components/tab-bar/index.vue b/passiflora-ui/src/components/tab-bar/index.vue new file mode 100644 index 0000000..609ba2a --- /dev/null +++ b/passiflora-ui/src/components/tab-bar/index.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/passiflora-ui/src/components/tab-bar/readme.md b/passiflora-ui/src/components/tab-bar/readme.md new file mode 100644 index 0000000..cea5c93 --- /dev/null +++ b/passiflora-ui/src/components/tab-bar/readme.md @@ -0,0 +1,12 @@ +## 组件说明 + +该组件非官方最终设计规范,以单独组件存在。 + +同时仅仅提供最基本的功能,后续进行优化及更改。 + + +## Component description + +The component unofficial final design specification exists as a separate component. + +At the same time, only the most basic functions are provided, and subsequent optimizations and changes will be made. \ No newline at end of file diff --git a/passiflora-ui/src/components/tab-bar/tab-item.vue b/passiflora-ui/src/components/tab-bar/tab-item.vue new file mode 100644 index 0000000..bc2014d --- /dev/null +++ b/passiflora-ui/src/components/tab-bar/tab-item.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/passiflora-ui/src/config/settings.json b/passiflora-ui/src/config/settings.json new file mode 100644 index 0000000..f8df9ac --- /dev/null +++ b/passiflora-ui/src/config/settings.json @@ -0,0 +1,17 @@ +{ + "theme": "light", + "colorWeak": false, + "navbar": true, + "menu": true, + "topMenu": false, + "hideMenu": false, + "menuCollapse": false, + "footer": false, + "themeColor": "#165DFF", + "menuWidth": 220, + "globalSettings": false, + "device": "desktop", + "tabBar": false, + "menuFromServer": true, + "serverMenu": [] +} diff --git a/passiflora-ui/src/directive/index.ts b/passiflora-ui/src/directive/index.ts new file mode 100644 index 0000000..85c567f --- /dev/null +++ b/passiflora-ui/src/directive/index.ts @@ -0,0 +1,8 @@ +import { App } from 'vue'; +import permission from './permission'; + +export default { + install(Vue: App) { + Vue.directive('permission', permission); + }, +}; diff --git a/passiflora-ui/src/directive/permission/index.ts b/passiflora-ui/src/directive/permission/index.ts new file mode 100644 index 0000000..8ea8643 --- /dev/null +++ b/passiflora-ui/src/directive/permission/index.ts @@ -0,0 +1,30 @@ +import { DirectiveBinding } from 'vue'; +import { useUserStore } from '@/store'; + +function checkPermission(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + const userStore = useUserStore(); + const { permission } = userStore; + + if (Array.isArray(value)) { + if (value.length > 0) { + const hasPermission = permission.includes(value as unknown as string); + if (!hasPermission && el.parentNode) { + el.parentNode.removeChild(el); + } + } + } else { + throw new Error( + `need permission! Like v-permission="['system:user:add','system:user:delete']"` + ); + } +} + +export default { + mounted(el: HTMLElement, binding: DirectiveBinding) { + checkPermission(el, binding); + }, + updated(el: HTMLElement, binding: DirectiveBinding) { + checkPermission(el, binding); + }, +}; diff --git a/passiflora-ui/src/env.d.ts b/passiflora-ui/src/env.d.ts new file mode 100644 index 0000000..6b7fc7d --- /dev/null +++ b/passiflora-ui/src/env.d.ts @@ -0,0 +1,11 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} +interface ImportMetaEnv { + readonly VITE_API_BASE_URL: string; +} diff --git a/passiflora-ui/src/hooks/chart-option.ts b/passiflora-ui/src/hooks/chart-option.ts new file mode 100644 index 0000000..13c3926 --- /dev/null +++ b/passiflora-ui/src/hooks/chart-option.ts @@ -0,0 +1,27 @@ +import { computed } from 'vue'; +import { EChartsOption } from 'echarts'; +import { useAppStore } from '@/store'; + +// for code hints +// import { SeriesOption } from 'echarts'; +// Because there are so many configuration items, this provides a relatively convenient code hint. +// When using vue, pay attention to the reactive issues. It is necessary to ensure that corresponding functions can be triggered, TypeScript does not report errors, and code writing is convenient. +interface optionsFn { + (isDark: boolean): EChartsOption; +} + +export default function useChartOption(sourceOption: optionsFn) { + const appStore = useAppStore(); + const isDark = computed(() => { + return appStore.theme === 'dark'; + }); + // echarts support https://echarts.apache.org/zh/theme-builder.html + // It's not used here + // TODO echarts themes + const chartOption = computed(() => { + return sourceOption(isDark.value); + }); + return { + chartOption, + }; +} diff --git a/passiflora-ui/src/hooks/loading.ts b/passiflora-ui/src/hooks/loading.ts new file mode 100644 index 0000000..a6f2768 --- /dev/null +++ b/passiflora-ui/src/hooks/loading.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue'; + +export default function useLoading(initValue = false) { + const loading = ref(initValue); + const setLoading = (value: boolean) => { + loading.value = value; + }; + const toggle = () => { + loading.value = !loading.value; + }; + return { + loading, + setLoading, + toggle, + }; +} diff --git a/passiflora-ui/src/hooks/locale.ts b/passiflora-ui/src/hooks/locale.ts new file mode 100644 index 0000000..afe4bf7 --- /dev/null +++ b/passiflora-ui/src/hooks/locale.ts @@ -0,0 +1,22 @@ +import { computed } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { Message } from '@arco-design/web-vue'; + +export default function useLocale() { + const i18 = useI18n(); + const currentLocale = computed(() => { + return i18.locale.value; + }); + const changeLocale = (value: string) => { + if (i18.locale.value === value) { + return; + } + i18.locale.value = value; + localStorage.setItem('arco-locale', value); + Message.success(i18.t('navbar.action.locale')); + }; + return { + currentLocale, + changeLocale, + }; +} diff --git a/passiflora-ui/src/hooks/permission.ts b/passiflora-ui/src/hooks/permission.ts new file mode 100644 index 0000000..9d45efc --- /dev/null +++ b/passiflora-ui/src/hooks/permission.ts @@ -0,0 +1,17 @@ +import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'; +import { useUserStore } from '@/store'; + +export default function usePermission() { + const userStore = useUserStore(); + return { + accessRouter(route: RouteLocationNormalized | RouteRecordRaw) { + return ( + !route.meta?.requiresAuth || + userStore.menu?.some( + (item) => item.toLowerCase() === (route.name as string).toLowerCase() + ) + ); + }, + // You can add any rules you want + }; +} diff --git a/passiflora-ui/src/hooks/request.ts b/passiflora-ui/src/hooks/request.ts new file mode 100644 index 0000000..961ecbb --- /dev/null +++ b/passiflora-ui/src/hooks/request.ts @@ -0,0 +1,26 @@ +import { ref, UnwrapRef } from 'vue'; +import { AxiosResponse } from 'axios'; +import useLoading from './loading'; +import { Result } from '@/types/global'; + +// use to fetch list +// Don't use async function. It doesn't work in async function. +// Use the bind function to add parameters +// example: useRequest(api.bind(null, {})) + +export default function useRequest( + api: () => Promise>, + defaultValue = [] as unknown as T, + isLoading = true +) { + const { loading, setLoading } = useLoading(isLoading); + const response = ref(defaultValue); + api() + .then((res) => { + response.value = res.data as unknown as UnwrapRef; + }) + .finally(() => { + setLoading(false); + }); + return { loading, response }; +} diff --git a/passiflora-ui/src/hooks/responsive.ts b/passiflora-ui/src/hooks/responsive.ts new file mode 100644 index 0000000..eefee8e --- /dev/null +++ b/passiflora-ui/src/hooks/responsive.ts @@ -0,0 +1,32 @@ +import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue'; +import { useDebounceFn } from '@vueuse/core'; +import { useAppStore } from '@/store'; +import { addEventListen, removeEventListen } from '@/utils/event'; + +const WIDTH = 992; // https://arco.design/vue/component/grid#responsivevalue + +function queryDevice() { + const rect = document.body.getBoundingClientRect(); + return rect.width - 1 < WIDTH; +} + +export default function useResponsive(immediate?: boolean) { + const appStore = useAppStore(); + function resizeHandler() { + if (!document.hidden) { + const isMobile = queryDevice(); + appStore.toggleDevice(isMobile ? 'mobile' : 'desktop'); + appStore.toggleMenu(isMobile); + } + } + const debounceFn = useDebounceFn(resizeHandler, 100); + onMounted(() => { + if (immediate) debounceFn(); + }); + onBeforeMount(() => { + addEventListen(window, 'resize', debounceFn); + }); + onBeforeUnmount(() => { + removeEventListen(window, 'resize', debounceFn); + }); +} diff --git a/passiflora-ui/src/hooks/themes.ts b/passiflora-ui/src/hooks/themes.ts new file mode 100644 index 0000000..7ce85b7 --- /dev/null +++ b/passiflora-ui/src/hooks/themes.ts @@ -0,0 +1,12 @@ +import { computed } from 'vue'; +import { useAppStore } from '@/store'; + +export default function useThemes() { + const appStore = useAppStore(); + const isDark = computed(() => { + return appStore.theme === 'dark'; + }); + return { + isDark, + }; +} diff --git a/passiflora-ui/src/hooks/user.ts b/passiflora-ui/src/hooks/user.ts new file mode 100644 index 0000000..a409e16 --- /dev/null +++ b/passiflora-ui/src/hooks/user.ts @@ -0,0 +1,19 @@ +import { useRouter } from 'vue-router'; +import { Message } from '@arco-design/web-vue'; + +import { useUserStore } from '@/store'; + +export default function useUser() { + const router = useRouter(); + const userStore = useUserStore(); + const logout = async (logoutTo?: string) => { + await userStore.logout(); + Message.success('登出成功'); + router.push({ + name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login', + }); + }; + return { + logout, + }; +} diff --git a/passiflora-ui/src/hooks/visible.ts b/passiflora-ui/src/hooks/visible.ts new file mode 100644 index 0000000..211f90d --- /dev/null +++ b/passiflora-ui/src/hooks/visible.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue'; + +export default function useVisible(initValue = false) { + const visible = ref(initValue); + const setVisible = (value: boolean) => { + visible.value = value; + }; + const toggle = () => { + visible.value = !visible.value; + }; + return { + visible, + setVisible, + toggle, + }; +} diff --git a/passiflora-ui/src/layout/default-layout.vue b/passiflora-ui/src/layout/default-layout.vue new file mode 100644 index 0000000..0d7d7bf --- /dev/null +++ b/passiflora-ui/src/layout/default-layout.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/passiflora-ui/src/layout/page-layout.vue b/passiflora-ui/src/layout/page-layout.vue new file mode 100644 index 0000000..5f60b36 --- /dev/null +++ b/passiflora-ui/src/layout/page-layout.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/passiflora-ui/src/locale/en-US.ts b/passiflora-ui/src/locale/en-US.ts new file mode 100644 index 0000000..c9c5a8e --- /dev/null +++ b/passiflora-ui/src/locale/en-US.ts @@ -0,0 +1,35 @@ +import localeMessageBox from '@/components/message-box/locale/en-US'; +import localeLogin from '@/views/login/locale/en-US'; + +import localeWorkplace from '@/views/dashboard/workplace/locale/en-US'; + +import localeSettings from './en-US/settings'; + +export default { + 'menu.dashboard': 'Dashboard', + 'menu.server.dashboard': 'Dashboard-Server', + 'menu.server.workplace': 'Workplace-Server', + 'menu.server.monitor': 'Monitor-Server', + 'menu.list': 'List', + 'menu.result': 'Result', + 'menu.exception': 'Exception', + 'menu.form': 'Form', + 'menu.profile': 'Profile', + 'menu.visualization': 'Data Visualization', + 'menu.user': 'User Center', + 'menu.arcoWebsite': 'Passiflora', + 'menu.faq': 'FAQ', + 'menu.system': 'System Setting', + 'menu.system.dict': 'Dict management', + 'menu.system.permission': 'Permission management', + 'menu.organization.position': 'Position management', + 'menu.organization': 'Organizational management', + 'menu.organization.user': 'User management', + 'menu.organization.org': 'Org management', + 'navbar.docs': 'Docs', + 'navbar.action.locale': 'Switch to English', + ...localeSettings, + ...localeMessageBox, + ...localeLogin, + ...localeWorkplace, +}; diff --git a/passiflora-ui/src/locale/en-US/settings.ts b/passiflora-ui/src/locale/en-US/settings.ts new file mode 100644 index 0000000..f1618bf --- /dev/null +++ b/passiflora-ui/src/locale/en-US/settings.ts @@ -0,0 +1,30 @@ +export default { + 'settings.title': 'Settings', + 'settings.themeColor': 'Theme Color', + 'settings.content': 'Content Setting', + 'settings.search': 'Search', + 'settings.language': 'Language', + 'settings.navbar': 'Navbar', + 'settings.menuWidth': 'Menu Width (px)', + 'settings.navbar.theme.toLight': 'Click to use light mode', + 'settings.navbar.theme.toDark': 'Click to use dark mode', + 'settings.navbar.screen.toFull': 'Click to switch to full screen mode', + 'settings.navbar.screen.toExit': 'Click to exit the full screen mode', + 'settings.navbar.alerts': 'alerts', + 'settings.menu': 'Menu', + 'settings.topMenu': 'Top Menu', + 'settings.tabBar': 'Tab Bar', + 'settings.footer': 'Footer', + 'settings.otherSettings': 'Other Settings', + 'settings.colorWeak': 'Color Weak', + 'settings.alertContent': + 'After the configuration is only temporarily effective, if you want to really affect the project, click the "Copy Settings" button below and replace the configuration in settings.json.', + 'settings.copySettings': 'Copy Settings', + 'settings.save': 'save', + 'settings.copySettings.message': + 'Copy succeeded, please paste to file src/settings.json.', + 'settings.close': 'Close', + 'settings.color.tooltip': + '10 gradient colors generated according to the theme color', + 'settings.menuFromServer': 'Menu From Server', +}; diff --git a/passiflora-ui/src/locale/index.ts b/passiflora-ui/src/locale/index.ts new file mode 100644 index 0000000..68349ce --- /dev/null +++ b/passiflora-ui/src/locale/index.ts @@ -0,0 +1,22 @@ +import { createI18n } from 'vue-i18n'; +import en from './en-US'; +import cn from './zh-CN'; + +export const LOCALE_OPTIONS = [ + { label: '中文', value: 'zh-CN' }, + { label: 'English', value: 'en-US' }, +]; +const defaultLocale = localStorage.getItem('arco-locale') || 'zh-CN'; + +const i18n = createI18n({ + locale: defaultLocale, + fallbackLocale: 'en-US', + legacy: false, + allowComposition: true, + messages: { + 'en-US': en, + 'zh-CN': cn, + }, +}); + +export default i18n; diff --git a/passiflora-ui/src/locale/zh-CN.ts b/passiflora-ui/src/locale/zh-CN.ts new file mode 100644 index 0000000..9948d81 --- /dev/null +++ b/passiflora-ui/src/locale/zh-CN.ts @@ -0,0 +1,35 @@ +import localeMessageBox from '@/components/message-box/locale/zh-CN'; +import localeLogin from '@/views/login/locale/zh-CN'; + +import localeWorkplace from '@/views/dashboard/workplace/locale/zh-CN'; + +import localeSettings from './zh-CN/settings'; + +export default { + 'menu.dashboard': '仪表盘', + 'menu.server.dashboard': '仪表盘-服务端', + 'menu.server.workplace': '工作台-服务端', + 'menu.server.monitor': '实时监控-服务端', + 'menu.list': '列表页', + 'menu.result': '结果页', + 'menu.exception': '异常页', + 'menu.form': '表单页', + 'menu.profile': '详情页', + 'menu.visualization': '数据可视化', + 'menu.user': '个人中心', + 'menu.arcoWebsite': 'Passiflora', + 'menu.faq': '常见问题', + 'menu.system': '系统管理', + 'menu.system.dict': '字典管理', + 'menu.system.permission': '权限管理', + 'menu.organization.position': '职位管理', + 'menu.organization': '组织管理', + 'menu.organization.user': '用户管理', + 'menu.organization.org': '机构管理', + 'navbar.docs': '文档中心', + 'navbar.action.locale': '切换为中文', + ...localeSettings, + ...localeMessageBox, + ...localeLogin, + ...localeWorkplace, +}; diff --git a/passiflora-ui/src/locale/zh-CN/settings.ts b/passiflora-ui/src/locale/zh-CN/settings.ts new file mode 100644 index 0000000..b954721 --- /dev/null +++ b/passiflora-ui/src/locale/zh-CN/settings.ts @@ -0,0 +1,30 @@ +export default { + 'settings.title': '页面配置', + 'settings.themeColor': '主题色', + 'settings.content': '内容区域', + 'settings.search': '搜索', + 'settings.language': '语言', + 'settings.navbar': '导航栏', + 'settings.menuWidth': '菜单宽度 (px)', + 'settings.navbar.theme.toLight': '点击切换为亮色模式', + 'settings.navbar.theme.toDark': '点击切换为暗黑模式', + 'settings.navbar.screen.toFull': '点击切换全屏模式', + 'settings.navbar.screen.toExit': '点击退出全屏模式', + 'settings.navbar.alerts': '消息通知', + 'settings.menu': '菜单栏', + 'settings.topMenu': '顶部菜单栏', + 'settings.tabBar': '多页签', + 'settings.footer': '底部', + 'settings.otherSettings': '其他设置', + 'settings.colorWeak': '色弱模式', + 'settings.alertContent': + '配置之后仅是临时生效,要想真正作用于项目,点击下方的 "复制配置" 按钮,将配置替换到 settings.json 中即可。', + 'settings.copySettings': '复制配置', + 'settings.save': '保存', + 'settings.copySettings.message': + '复制成功,请粘贴到 src/settings.json 文件中', + 'settings.close': '关闭', + 'settings.color.tooltip': + '根据主题颜色生成的 10 个梯度色(将配置复制到项目中,主题色才能对亮色 / 暗黑模式同时生效)', + 'settings.menuFromServer': '菜单来源于后台', +}; diff --git a/passiflora-ui/src/main.ts b/passiflora-ui/src/main.ts new file mode 100644 index 0000000..baae97c --- /dev/null +++ b/passiflora-ui/src/main.ts @@ -0,0 +1,27 @@ +import { createApp } from 'vue'; +import ArcoVue from '@arco-design/web-vue'; +import ArcoVueIcon from '@arco-design/web-vue/es/icon'; +import globalComponents from '@/components'; +import router from './router'; +import store from './store'; +import i18n from './locale'; +import directive from './directive'; +import App from './App.vue'; +// Styles are imported via arco-plugin. See config/plugin/arcoStyleImport.ts in the directory for details +// 样式通过 arco-plugin 插件导入。详见目录文件 config/plugin/arcoStyleImport.ts +// https://arco.design/docs/designlab/use-theme-package +import '@/assets/style/global.less'; +import '@/api/interceptor'; + +const app = createApp(App); + +app.use(ArcoVue, {}); +app.use(ArcoVueIcon); + +app.use(router); +app.use(store); +app.use(i18n); +app.use(globalComponents); +app.use(directive); + +app.mount('#app'); diff --git a/passiflora-ui/src/router/app-menus/index.ts b/passiflora-ui/src/router/app-menus/index.ts new file mode 100644 index 0000000..6c71574 --- /dev/null +++ b/passiflora-ui/src/router/app-menus/index.ts @@ -0,0 +1,32 @@ +import { RouteRecordNormalized } from 'vue-router'; +import { appRoutes, appExternalRoutes } from '../routes'; + +const mixinRoutes = [...appRoutes, ...appExternalRoutes]; + +const appClientMenus = mixinRoutes.map((el) => { + const { name, path, meta, redirect, children } = el; + return { + name, + path, + meta, + redirect, + children, + }; +}); + +export const menuNameLocaleMap = new Map(); + +const loop = (menus: RouteRecordNormalized[], map: Map) => { + if (!menus || !menus.length) { + return; + } + menus.forEach((menu) => { + if (menu.name && menu.meta?.locale) { + map.set(menu.name as string, menu.meta?.locale as string); + } + loop(menu.children as RouteRecordNormalized[], map); + }); +}; + +loop(appClientMenus as RouteRecordNormalized[], menuNameLocaleMap); +export default appClientMenus; diff --git a/passiflora-ui/src/router/constants.ts b/passiflora-ui/src/router/constants.ts new file mode 100644 index 0000000..0488d40 --- /dev/null +++ b/passiflora-ui/src/router/constants.ts @@ -0,0 +1,17 @@ +export const NOT_FOUND = { + name: 'notFound', +}; + +export const NOT_PERMISSION = { + name: 'notPermission', +}; + +export const REDIRECT_ROUTE_NAME = 'redirect'; + +export const DEFAULT_ROUTE_NAME = 'workplace'; + +export const DEFAULT_ROUTE = { + title: 'menu.dashboard.workplace', + name: DEFAULT_ROUTE_NAME, + fullPath: '/dashboard/workplace', +}; diff --git a/passiflora-ui/src/router/guard/index.ts b/passiflora-ui/src/router/guard/index.ts new file mode 100644 index 0000000..bedc28d --- /dev/null +++ b/passiflora-ui/src/router/guard/index.ts @@ -0,0 +1,17 @@ +import type { Router } from 'vue-router'; +import { setRouteEmitter } from '@/utils/route-listener'; +import setupUserLoginInfoGuard from './userLoginInfo'; +import setupPermissionGuard from './permission'; + +function setupPageGuard(router: Router) { + router.beforeEach(async (to) => { + // emit route change + setRouteEmitter(to); + }); +} + +export default function createRouteGuard(router: Router) { + setupPageGuard(router); + setupUserLoginInfoGuard(router); + setupPermissionGuard(router); +} diff --git a/passiflora-ui/src/router/guard/permission.ts b/passiflora-ui/src/router/guard/permission.ts new file mode 100644 index 0000000..73ca841 --- /dev/null +++ b/passiflora-ui/src/router/guard/permission.ts @@ -0,0 +1,40 @@ +import type { Router, RouteRecordNormalized } from 'vue-router'; +import NProgress from 'nprogress'; // progress bar + +import usePermission from '@/hooks/permission'; +import { appRoutes } from '../routes'; +import { NOT_FOUND, NOT_PERMISSION } from '../constants'; + +export default function setupPermissionGuard(router: Router) { + router.beforeEach(async (to, from, next) => { + const Permission = usePermission(); + const permissionsAllow = Permission.accessRouter(to); + if (!to.meta.requiresAuth) { + next(); + NProgress.done(); + return; + } + + const serverMenuConfig = [...appRoutes]; + + let exist = false; + while (serverMenuConfig.length && !exist) { + const element = serverMenuConfig.shift(); + if (element?.name === to.name) exist = true; + + if (element?.children) { + serverMenuConfig.push( + ...(element.children as unknown as RouteRecordNormalized[]) + ); + } + } + if (exist && permissionsAllow) { + next(); + } else if (!exist) { + next(NOT_FOUND); + } else { + next(NOT_PERMISSION); + } + NProgress.done(); + }); +} diff --git a/passiflora-ui/src/router/guard/userLoginInfo.ts b/passiflora-ui/src/router/guard/userLoginInfo.ts new file mode 100644 index 0000000..96155ac --- /dev/null +++ b/passiflora-ui/src/router/guard/userLoginInfo.ts @@ -0,0 +1,45 @@ +import type { Router, LocationQueryRaw } from 'vue-router'; +import NProgress from 'nprogress'; // progress bar + +import { useAppStore, useUserStore } from '@/store'; +import { isLogin } from '@/utils/auth'; + +export default function setupUserLoginInfoGuard(router: Router) { + router.beforeEach(async (to, from, next) => { + NProgress.start(); + const userStore = useUserStore(); + const appStore = useAppStore(); + if (isLogin()) { + if (userStore.userId) { + next(); + } else { + try { + await userStore.info(); + await appStore.fetchServerMenuConfig(); + next(); + } catch (error) { + await userStore.logout(); + next({ + name: 'login', + query: { + redirect: to.name, + ...to.query, + } as LocationQueryRaw, + }); + } + } + } else { + if (!to.meta.requiresAuth) { + next(); + return; + } + next({ + name: 'login', + query: { + redirect: to.name, + ...to.query, + } as LocationQueryRaw, + }); + } + }); +} diff --git a/passiflora-ui/src/router/index.ts b/passiflora-ui/src/router/index.ts new file mode 100644 index 0000000..ee8e62f --- /dev/null +++ b/passiflora-ui/src/router/index.ts @@ -0,0 +1,44 @@ +import { createRouter, createWebHistory } from 'vue-router'; +import NProgress from 'nprogress'; // progress bar +import 'nprogress/nprogress.css'; + +import { appRoutes } from './routes'; +import { + REDIRECT_MAIN, + NOT_FOUND_ROUTE, + NOT_PERMISSION_ROUTE, + NOT_AUTH_ROUTE, +} from './routes/base'; +import createRouteGuard from './guard'; + +NProgress.configure({ showSpinner: false }); // NProgress Configuration + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/', + redirect: 'login', + }, + { + path: '/login', + name: 'login', + component: () => import('@/views/login/index.vue'), + meta: { + requiresAuth: false, + }, + }, + ...appRoutes, + REDIRECT_MAIN, + NOT_FOUND_ROUTE, + NOT_PERMISSION_ROUTE, + NOT_AUTH_ROUTE, + ], + scrollBehavior() { + return { top: 0 }; + }, +}); + +createRouteGuard(router); + +export default router; diff --git a/passiflora-ui/src/router/routes/base.ts b/passiflora-ui/src/router/routes/base.ts new file mode 100644 index 0000000..f0ed1d8 --- /dev/null +++ b/passiflora-ui/src/router/routes/base.ts @@ -0,0 +1,55 @@ +import type { RouteRecordRaw } from 'vue-router'; +import { REDIRECT_ROUTE_NAME } from '@/router/constants'; + +export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue'); + +export const REDIRECT_MAIN: RouteRecordRaw = { + path: '/redirect', + name: 'redirectWrapper', + component: DEFAULT_LAYOUT, + meta: { + requiresAuth: true, + hideInMenu: true, + }, + children: [ + { + path: '/redirect/:path', + name: REDIRECT_ROUTE_NAME, + component: () => import('@/views/redirect/index.vue'), + meta: { + requiresAuth: true, + hideInMenu: true, + }, + }, + ], +}; + +export const NOT_FOUND_ROUTE: RouteRecordRaw = { + path: '/:pathMatch(.*)*', + name: 'notFound', + component: () => import('@/views/not-found/index.vue'), + meta: { + requiresAuth: false, + hideInMenu: true, + }, +}; + +export const NOT_PERMISSION_ROUTE: RouteRecordRaw = { + path: '/notPermission', + name: 'notPermission', + component: () => import('@/views/not-permission/index.vue'), + meta: { + requiresAuth: false, + hideInMenu: true, + }, +}; + +export const NOT_AUTH_ROUTE: RouteRecordRaw = { + path: '/notAuth', + name: 'notAuth', + component: () => import('@/views/not-auth/index.vue'), + meta: { + requiresAuth: false, + hideInMenu: true, + }, +}; diff --git a/passiflora-ui/src/router/routes/index.ts b/passiflora-ui/src/router/routes/index.ts new file mode 100644 index 0000000..c60f695 --- /dev/null +++ b/passiflora-ui/src/router/routes/index.ts @@ -0,0 +1,25 @@ +import type { RouteRecordNormalized } from 'vue-router'; + +const modules = import.meta.glob('./modules/*.ts', { eager: true }); +const externalModules = import.meta.glob('./externalModules/*.ts', { + eager: true, +}); + +function formatModules(_modules: any, result: RouteRecordNormalized[]) { + Object.keys(_modules).forEach((key) => { + const defaultModule = _modules[key].default; + if (!defaultModule) return; + const moduleList = Array.isArray(defaultModule) + ? [...defaultModule] + : [defaultModule]; + result.push(...moduleList); + }); + return result; +} + +export const appRoutes: RouteRecordNormalized[] = formatModules(modules, []); + +export const appExternalRoutes: RouteRecordNormalized[] = formatModules( + externalModules, + [] +); diff --git a/passiflora-ui/src/router/routes/modules/dashboard.ts b/passiflora-ui/src/router/routes/modules/dashboard.ts new file mode 100644 index 0000000..b92c836 --- /dev/null +++ b/passiflora-ui/src/router/routes/modules/dashboard.ts @@ -0,0 +1,27 @@ +import { DEFAULT_LAYOUT } from '../base'; +import { AppRouteRecordRaw } from '../types'; + +const DASHBOARD: AppRouteRecordRaw = { + path: '/dashboard', + name: 'dashboard', + component: DEFAULT_LAYOUT, + meta: { + locale: 'menu.dashboard', + icon: 'icon-dashboard', + order: 0, + requiresAuth: true, + }, + children: [ + { + path: 'workplace', + name: 'workplace', + component: () => import('@/views/dashboard/workplace/index.vue'), + meta: { + locale: 'menu.dashboard.workplace', + requiresAuth: true, + }, + }, + ], +}; + +export default DASHBOARD; diff --git a/passiflora-ui/src/router/routes/modules/organization.ts b/passiflora-ui/src/router/routes/modules/organization.ts new file mode 100644 index 0000000..e90397b --- /dev/null +++ b/passiflora-ui/src/router/routes/modules/organization.ts @@ -0,0 +1,44 @@ +import { DEFAULT_LAYOUT } from '../base'; +import { AppRouteRecordRaw } from '../types'; + +const SYSTEM: AppRouteRecordRaw = { + path: '/organization', + name: 'organization', + component: DEFAULT_LAYOUT, + meta: { + locale: 'menu.organization', + icon: 'icon-user-group', + requiresAuth: true, + }, + children: [ + { + path: 'user', + name: 'user', + component: () => import('@/views/organization/user/index.vue'), + meta: { + locale: 'menu.organization.user', + requiresAuth: true, + }, + }, + { + path: 'org', + name: 'org', + component: () => import('@/views/organization/org/index.vue'), + meta: { + locale: 'menu.organization.org', + requiresAuth: true, + }, + }, + { + path: 'position', + name: 'position', + component: () => import('@/views/organization/position/index.vue'), + meta: { + locale: 'menu.organization.position', + requiresAuth: true, + }, + }, + ], +}; + +export default SYSTEM; diff --git a/passiflora-ui/src/router/routes/modules/system.ts b/passiflora-ui/src/router/routes/modules/system.ts new file mode 100644 index 0000000..f0661f3 --- /dev/null +++ b/passiflora-ui/src/router/routes/modules/system.ts @@ -0,0 +1,36 @@ +import { DEFAULT_LAYOUT } from '../base'; +import { AppRouteRecordRaw } from '../types'; + +const SYSTEM: AppRouteRecordRaw = { + path: '/system', + name: 'system', + component: DEFAULT_LAYOUT, + meta: { + title: '系统管理-local优先', + locale: 'menu.system', + icon: 'icon-settings', + requiresAuth: true, + }, + children: [ + { + path: 'dict', + name: 'dict', + component: () => import('@/views/system/dict/index.vue'), + meta: { + locale: 'menu.system.dict', + requiresAuth: true, + }, + }, + { + path: 'permission', + name: 'permission', + component: () => import('@/views/system/permission/index.vue'), + meta: { + locale: 'menu.system.permission', + requiresAuth: true, + }, + }, + ], +}; + +export default SYSTEM; diff --git a/passiflora-ui/src/router/routes/types.ts b/passiflora-ui/src/router/routes/types.ts new file mode 100644 index 0000000..6b8e8d7 --- /dev/null +++ b/passiflora-ui/src/router/routes/types.ts @@ -0,0 +1,20 @@ +import { defineComponent } from 'vue'; +import type { RouteMeta, NavigationGuard } from 'vue-router'; + +export type Component = + | ReturnType + | (() => Promise) + | (() => Promise); + +export interface AppRouteRecordRaw { + path: string; + name?: string | symbol; + meta?: RouteMeta; + redirect?: string; + component: Component | string; + children?: AppRouteRecordRaw[]; + alias?: string | string[]; + props?: Record; + beforeEnter?: NavigationGuard | NavigationGuard[]; + fullPath?: string; +} diff --git a/passiflora-ui/src/router/typings.d.ts b/passiflora-ui/src/router/typings.d.ts new file mode 100644 index 0000000..214be6b --- /dev/null +++ b/passiflora-ui/src/router/typings.d.ts @@ -0,0 +1,15 @@ +import 'vue-router'; + +declare module 'vue-router' { + interface RouteMeta { + requiresAuth: boolean; // Whether login is required to access the current page (every route must declare) + icon?: string; // The icon show in the side permission + locale?: string; // The locale name show in side permission and breadcrumb + hideInMenu?: boolean; // If true, it is not displayed in the side permission + hideChildrenInMenu?: boolean; // if set true, the children are not displayed in the side permission + activeMenu?: string; // if set name, the permission will be highlighted according to the name you set + order?: number; // Sort routing permission items. If set key, the higher the value, the more forward it is + noAffix?: boolean; // if set true, the tag will not affix in the tab-bar + ignoreCache?: boolean; // if set true, the page will not be cached + } +} diff --git a/passiflora-ui/src/store/index.ts b/passiflora-ui/src/store/index.ts new file mode 100644 index 0000000..480991a --- /dev/null +++ b/passiflora-ui/src/store/index.ts @@ -0,0 +1,17 @@ +import { createPinia } from 'pinia'; +import useAppStore from './modules/app'; +import useUserStore from './modules/user'; +import useTabBarStore from './modules/tab-bar'; +import useEnumStore from './modules/enum'; +import useDictStore from './modules/dict'; + +const pinia = createPinia(); + +export { + useAppStore, + useUserStore, + useTabBarStore, + useEnumStore, + useDictStore, +}; +export default pinia; diff --git a/passiflora-ui/src/store/modules/app/index.ts b/passiflora-ui/src/store/modules/app/index.ts new file mode 100644 index 0000000..728fe95 --- /dev/null +++ b/passiflora-ui/src/store/modules/app/index.ts @@ -0,0 +1,76 @@ +import { defineStore } from 'pinia'; +import type { RouteRecordNormalized } from 'vue-router'; +import defaultSettings from '@/config/settings.json'; +import { getMenuList } from '@/api/organization/user'; +import { menuNameLocaleMap } from '@/router/app-menus'; +import { AppState } from './types'; + +const localSetting = localStorage.getItem('globalSettings'); +const useSetting = localSetting ? JSON.parse(localSetting) : defaultSettings; + +// 色弱模式配置 +document.body.style.filter = useSetting.colorWeak ? 'invert(80%)' : 'none'; + +const useAppStore = defineStore('app', { + state: (): AppState => ({ ...useSetting }), + + getters: { + appCurrentSetting(state: AppState): AppState { + return { ...state }; + }, + appDevice(state: AppState) { + return state.device; + }, + appAsyncMenus(state: AppState): RouteRecordNormalized[] { + return state.serverMenu; + }, + }, + + actions: { + // Update app settings + updateSettings(partial: Partial) { + // @ts-ignore-next-line + this.$patch(partial); + }, + + // Change theme color + toggleTheme(dark: boolean) { + if (dark) { + this.theme = 'dark'; + document.body.setAttribute('arco-theme', 'dark'); + } else { + this.theme = 'light'; + document.body.removeAttribute('arco-theme'); + } + }, + toggleDevice(device: string) { + this.device = device; + }, + toggleMenu(value: boolean) { + this.hideMenu = value; + }, + async fetchServerMenuConfig() { + const { data } = await getMenuList(); + this.serverMenu = data.data; + await this.loop(this.serverMenu); + }, + + async loop(appClientMenus: RouteRecordNormalized[]) { + if (!appClientMenus || !appClientMenus.length) { + return; + } + appClientMenus.forEach((menu) => { + if (menuNameLocaleMap.has(menu.name)) { + menu.meta.locale = menuNameLocaleMap.get(menu.name); + } + this.loop(menu.children as RouteRecordNormalized[]); + }); + }, + + clearServerMenu() { + this.serverMenu = []; + }, + }, +}); + +export default useAppStore; diff --git a/passiflora-ui/src/store/modules/app/types.ts b/passiflora-ui/src/store/modules/app/types.ts new file mode 100644 index 0000000..4229f83 --- /dev/null +++ b/passiflora-ui/src/store/modules/app/types.ts @@ -0,0 +1,20 @@ +import type { RouteRecordNormalized } from 'vue-router'; + +export interface AppState { + theme: string; + colorWeak: boolean; + navbar: boolean; + menu: boolean; + topMenu: boolean; + hideMenu: boolean; + menuCollapse: boolean; + footer: boolean; + themeColor: string; + menuWidth: number; + globalSettings: boolean; + device: string; + tabBar: boolean; + menuFromServer: boolean; + serverMenu: RouteRecordNormalized[]; + [key: string]: unknown; +} diff --git a/passiflora-ui/src/store/modules/dict/index.ts b/passiflora-ui/src/store/modules/dict/index.ts new file mode 100644 index 0000000..01d5a85 --- /dev/null +++ b/passiflora-ui/src/store/modules/dict/index.ts @@ -0,0 +1,26 @@ +import { defineStore } from 'pinia'; +import { DictItemRecord, dictItemListByDictTag } from '@/api/system/dict'; + +const useDictStore = defineStore('dict', { + state: () => ({ + cacheMap: new Map(), + }), + actions: { + async getDictItems(dictTag: string, isNumber: boolean) { + if (this.cacheMap.has(dictTag)) { + return this.cacheMap.get(dictTag); + } + const { data } = await dictItemListByDictTag(dictTag); + if (isNumber && data.data) { + const items = data.data as unknown as DictItemRecord[]; + items.forEach((item) => { + item.value = parseInt(item.value as string, 10); + }); + } + this.cacheMap.set(dictTag, data.data); + return data.data; + }, + }, +}); + +export default useDictStore; diff --git a/passiflora-ui/src/store/modules/enum/index.ts b/passiflora-ui/src/store/modules/enum/index.ts new file mode 100644 index 0000000..fef647d --- /dev/null +++ b/passiflora-ui/src/store/modules/enum/index.ts @@ -0,0 +1,20 @@ +import { defineStore } from 'pinia'; +import { EnumRecord, list } from '@/api/system/enum'; + +const useEnumStore = defineStore('enums', { + state: () => ({ + cacheMap: new Map(), + }), + actions: { + async getEnums(key: string) { + if (this.cacheMap.has(key)) { + return this.cacheMap.get(key); + } + const { data } = await list(key); + this.cacheMap.set(key, data.data); + return data.data; + }, + }, +}); + +export default useEnumStore; diff --git a/passiflora-ui/src/store/modules/tab-bar/index.ts b/passiflora-ui/src/store/modules/tab-bar/index.ts new file mode 100644 index 0000000..6072e1d --- /dev/null +++ b/passiflora-ui/src/store/modules/tab-bar/index.ts @@ -0,0 +1,74 @@ +import type { RouteLocationNormalized } from 'vue-router'; +import { defineStore } from 'pinia'; +import { + DEFAULT_ROUTE, + DEFAULT_ROUTE_NAME, + REDIRECT_ROUTE_NAME, +} from '@/router/constants'; +import { isString } from '@/utils/is'; +import { TabBarState, TagProps } from './types'; + +const formatTag = (route: RouteLocationNormalized): TagProps => { + const { name, meta, fullPath, query } = route; + return { + title: meta.locale || '', + name: String(name), + fullPath, + query, + ignoreCache: meta.ignoreCache, + }; +}; + +const BAN_LIST = [REDIRECT_ROUTE_NAME]; + +const useAppStore = defineStore('tabBar', { + state: (): TabBarState => ({ + cacheTabList: new Set([DEFAULT_ROUTE_NAME]), + tagList: [DEFAULT_ROUTE], + }), + + getters: { + getTabList(): TagProps[] { + return this.tagList; + }, + getCacheList(): string[] { + return Array.from(this.cacheTabList); + }, + }, + + actions: { + updateTabList(route: RouteLocationNormalized) { + if (BAN_LIST.includes(route.name as string)) return; + this.tagList.push(formatTag(route)); + if (!route.meta.ignoreCache) { + this.cacheTabList.add(route.name as string); + } + }, + deleteTag(idx: number, tag: TagProps) { + this.tagList.splice(idx, 1); + this.cacheTabList.delete(tag.name); + }, + addCache(name: string) { + if (isString(name) && name !== '') this.cacheTabList.add(name); + }, + deleteCache(tag: TagProps) { + this.cacheTabList.delete(tag.name); + }, + freshTabList(tags: TagProps[]) { + this.tagList = tags; + this.cacheTabList.clear(); + // 要先判断ignoreCache + this.tagList + .filter((el) => !el.ignoreCache) + .map((el) => el.name) + .forEach((x) => this.cacheTabList.add(x)); + }, + resetTabList() { + this.tagList = [DEFAULT_ROUTE]; + this.cacheTabList.clear(); + this.cacheTabList.add(DEFAULT_ROUTE_NAME); + }, + }, +}); + +export default useAppStore; diff --git a/passiflora-ui/src/store/modules/tab-bar/types.ts b/passiflora-ui/src/store/modules/tab-bar/types.ts new file mode 100644 index 0000000..29c1c53 --- /dev/null +++ b/passiflora-ui/src/store/modules/tab-bar/types.ts @@ -0,0 +1,12 @@ +export interface TagProps { + title: string; + name: string; + fullPath: string; + query?: any; + ignoreCache?: boolean; +} + +export interface TabBarState { + tagList: TagProps[]; + cacheTabList: Set; +} diff --git a/passiflora-ui/src/store/modules/user/index.ts b/passiflora-ui/src/store/modules/user/index.ts new file mode 100644 index 0000000..17debd1 --- /dev/null +++ b/passiflora-ui/src/store/modules/user/index.ts @@ -0,0 +1,81 @@ +import { defineStore } from 'pinia'; +import { + login as userLogin, + logout as userLogout, + currentUserInfo, + LoginData, +} from '@/api/organization/user'; +import { setToken, clearToken } from '@/utils/auth'; +import { removeRouteListener } from '@/utils/route-listener'; +import { UserState } from './types'; +import useAppStore from '../app'; + +const useUserStore = defineStore('user', { + state: (): UserState => ({ + createBy: '', + updateBy: '', + createTime: '', + updateTime: '', + delFlag: 0, + version: 0, + userId: '', + userName: '', + gender: 0, + phoneNum: '', + email: '', + remark: '', + avatarFile: '', + permission: [], + menu: [], + }), + + getters: { + userInfo(state: UserState): UserState { + return { ...state }; + }, + }, + + actions: { + // Set user's information + setInfo(partial: Partial) { + this.$patch(partial); + }, + + // Reset user's information + resetInfo() { + this.$reset(); + }, + + // Get user's information + async info() { + const appStore = useAppStore(); + const res = await currentUserInfo(); + await appStore.fetchServerMenuConfig(); + this.setInfo(res.data.data); + }, + + // Login + async login(loginForm: LoginData) { + try { + const res = await userLogin(loginForm); + setToken(res.data.data); + await this.info(); + } catch (err) { + clearToken(); + throw err; + } + }, + + // Logout + async logout() { + await userLogout(); + const appStore = useAppStore(); + this.resetInfo(); + clearToken(); + removeRouteListener(); + appStore.clearServerMenu(); + }, + }, +}); + +export default useUserStore; diff --git a/passiflora-ui/src/store/modules/user/types.ts b/passiflora-ui/src/store/modules/user/types.ts new file mode 100644 index 0000000..0f46a8e --- /dev/null +++ b/passiflora-ui/src/store/modules/user/types.ts @@ -0,0 +1,18 @@ +export interface UserState { + createBy?: string; + updateBy?: string; + createTime?: string; + updateTime?: string; + delFlag?: number; + version?: number; + userId?: string; + userName?: string; + gender?: number; + phoneNum?: string; + email?: string; + remark?: string; + avatarFile?: string; + // TODO + permission: string[]; + menu: string[]; +} diff --git a/passiflora-ui/src/types/echarts.ts b/passiflora-ui/src/types/echarts.ts new file mode 100644 index 0000000..ac4578a --- /dev/null +++ b/passiflora-ui/src/types/echarts.ts @@ -0,0 +1,10 @@ +import { CallbackDataParams } from 'echarts/types/dist/shared'; + +export interface ToolTipFormatterParams extends CallbackDataParams { + axisDim: string; + axisIndex: number; + axisType: string; + axisId: string; + axisValue: string; + axisValueLabel: string; +} diff --git a/passiflora-ui/src/types/global.ts b/passiflora-ui/src/types/global.ts new file mode 100644 index 0000000..ac78113 --- /dev/null +++ b/passiflora-ui/src/types/global.ts @@ -0,0 +1,68 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; + +export interface AnyObject { + [key: string]: unknown; +} + +export interface Options { + value: unknown; + label: string; +} + +export interface NodeOptions extends Options { + children?: NodeOptions[]; +} + +export interface GetParams { + body: null; + type: string; + url: string; +} + +export interface PostData { + body: string; + type: string; + url: string; +} + +export interface Pagination { + current: number; + pageSize: number; + total?: number; +} + +export type TimeRanger = [string, string]; + +export interface GeneralChart { + xAxis: string[]; + data: Array<{ name: string; value: number[] }>; +} + +export interface Result { + code: number; + message: string; + data: T; +} + +export interface Page { + list: T[]; + total: number; +} + +export interface BaseEntity { + createBy?: string; + updateBy?: string; + createTime?: string; + updateTime?: string; + delFlag?: number; + version?: number; + disabled?: boolean; +} + +export interface BasePageParam { + current: number; + pageSize: number; +} + +export type SizeProps = 'mini' | 'small' | 'medium' | 'large'; +export type Column = TableColumnData & { checked?: true }; diff --git a/passiflora-ui/src/utils/auth.ts b/passiflora-ui/src/utils/auth.ts new file mode 100644 index 0000000..fbc98df --- /dev/null +++ b/passiflora-ui/src/utils/auth.ts @@ -0,0 +1,23 @@ +const TOKEN_KEY = 'Authorization'; + +const isLogin = () => { + return !!localStorage.getItem(TOKEN_KEY); +}; + +const getToken = () => { + return localStorage.getItem(TOKEN_KEY); +}; + +const hasToken = () => { + return !!localStorage.getItem(TOKEN_KEY); +}; + +const setToken = (token: string) => { + localStorage.setItem(TOKEN_KEY, token); +}; + +const clearToken = () => { + localStorage.removeItem(TOKEN_KEY); +}; + +export { isLogin, getToken, setToken, hasToken, clearToken }; diff --git a/passiflora-ui/src/utils/dict.ts b/passiflora-ui/src/utils/dict.ts new file mode 100644 index 0000000..f774ecb --- /dev/null +++ b/passiflora-ui/src/utils/dict.ts @@ -0,0 +1,16 @@ +import { DictItemRecord } from '@/api/system/dict'; + +export function getLabelByValue(items: DictItemRecord[], value: number) { + let label = ''; + items.forEach((item) => { + if ( + item.value !== undefined && + item.label !== undefined && + item.value.toString() === value.toString() + ) { + label = item.label; + } + }); + return label; +} +export default null; diff --git a/passiflora-ui/src/utils/enums.ts b/passiflora-ui/src/utils/enums.ts new file mode 100644 index 0000000..4e26a7e --- /dev/null +++ b/passiflora-ui/src/utils/enums.ts @@ -0,0 +1,13 @@ +import { EnumRecord } from '@/api/system/enum'; + +export function getLabelByValue(enums: EnumRecord[], value: number) { + let label = ''; + enums.forEach((e) => { + if (e.value === value) { + label = e.label; + } + }); + return label; +} + +export default null; diff --git a/passiflora-ui/src/utils/env.ts b/passiflora-ui/src/utils/env.ts new file mode 100644 index 0000000..4147b35 --- /dev/null +++ b/passiflora-ui/src/utils/env.ts @@ -0,0 +1,3 @@ +const debug = import.meta.env.MODE !== 'production'; + +export default debug; diff --git a/passiflora-ui/src/utils/event.ts b/passiflora-ui/src/utils/event.ts new file mode 100644 index 0000000..c700d3d --- /dev/null +++ b/passiflora-ui/src/utils/event.ts @@ -0,0 +1,27 @@ +export function addEventListen( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.addEventListener && + typeof target.addEventListener === 'function' + ) { + target.addEventListener(event, handler, capture); + } +} + +export function removeEventListen( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.removeEventListener && + typeof target.removeEventListener === 'function' + ) { + target.removeEventListener(event, handler, capture); + } +} diff --git a/passiflora-ui/src/utils/index.ts b/passiflora-ui/src/utils/index.ts new file mode 100644 index 0000000..c2632bc --- /dev/null +++ b/passiflora-ui/src/utils/index.ts @@ -0,0 +1,50 @@ +type TargetContext = '_self' | '_parent' | '_blank' | '_top'; + +export const openWindow = ( + url: string, + opts?: { target?: TargetContext; [key: string]: any } +) => { + const { target = '_blank', ...others } = opts || {}; + window.open( + url, + target, + Object.entries(others) + .reduce((preValue: string[], curValue) => { + const [key, value] = curValue; + return [...preValue, `${key}=${value}`]; + }, []) + .join(',') + ); +}; + +export const regexUrl = new RegExp( + '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', + 'i' +); + +export const densityList = [ + { + name: '迷你', + value: 'mini', + }, + { + name: '偏小', + value: 'small', + }, + { + name: '中等', + value: 'medium', + }, + { + name: '偏大', + value: 'large', + }, +]; + +export const rowSelection = { + type: 'checkbox', + showCheckedAll: true, + onlyCurrent: false, +}; + +export default null; diff --git a/passiflora-ui/src/utils/is.ts b/passiflora-ui/src/utils/is.ts new file mode 100644 index 0000000..99deea8 --- /dev/null +++ b/passiflora-ui/src/utils/is.ts @@ -0,0 +1,53 @@ +const opt = Object.prototype.toString; + +export function isArray(obj: any): obj is any[] { + return opt.call(obj) === '[object Array]'; +} + +export function isObject(obj: any): obj is { [key: string]: any } { + return opt.call(obj) === '[object Object]'; +} + +export function isString(obj: any): obj is string { + return opt.call(obj) === '[object String]'; +} + +export function isNumber(obj: any): obj is number { + return opt.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line +} + +export function isRegExp(obj: any) { + return opt.call(obj) === '[object RegExp]'; +} + +export function isFile(obj: any): obj is File { + return opt.call(obj) === '[object File]'; +} + +export function isBlob(obj: any): obj is Blob { + return opt.call(obj) === '[object Blob]'; +} + +export function isUndefined(obj: any): obj is undefined { + return obj === undefined; +} + +export function isNull(obj: any): obj is null { + return obj === null; +} + +export function isFunction(obj: any): obj is (...args: any[]) => any { + return typeof obj === 'function'; +} + +export function isEmptyObject(obj: any): boolean { + return isObject(obj) && Object.keys(obj).length === 0; +} + +export function isExist(obj: any): boolean { + return obj || obj === 0; +} + +export function isWindow(el: any): el is Window { + return el === window; +} diff --git a/passiflora-ui/src/utils/monitor.ts b/passiflora-ui/src/utils/monitor.ts new file mode 100644 index 0000000..7b49015 --- /dev/null +++ b/passiflora-ui/src/utils/monitor.ts @@ -0,0 +1,28 @@ +import { App, ComponentPublicInstance } from 'vue'; +import axios from 'axios'; + +export default function handleError(Vue: App, baseUrl: string) { + if (!baseUrl) { + return; + } + Vue.config.errorHandler = ( + err: unknown, + instance: ComponentPublicInstance | null, + info: string + ) => { + // send error info + axios.post(`${baseUrl}/report-error`, { + err, + instance, + info, + // location: window.location.href, + // message: err.message, + // stack: err.stack, + // browserInfo: getBrowserInfo(), + // user info + // dom info + // url info + // ... + }); + }; +} diff --git a/passiflora-ui/src/utils/objects.ts b/passiflora-ui/src/utils/objects.ts new file mode 100644 index 0000000..e69de29 diff --git a/passiflora-ui/src/utils/route-listener.ts b/passiflora-ui/src/utils/route-listener.ts new file mode 100644 index 0000000..0b79591 --- /dev/null +++ b/passiflora-ui/src/utils/route-listener.ts @@ -0,0 +1,31 @@ +/** + * Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management + * 单独监听路由会浪费渲染性能。使用发布订阅模式去进行分发管理。 + */ +import mitt, { Handler } from 'mitt'; +import type { RouteLocationNormalized } from 'vue-router'; + +const emitter = mitt(); + +const key = Symbol('ROUTE_CHANGE'); + +let latestRoute: RouteLocationNormalized; + +export function setRouteEmitter(to: RouteLocationNormalized) { + emitter.emit(key, to); + latestRoute = to; +} + +export function listenerRouteChange( + handler: (route: RouteLocationNormalized) => void, + immediate = true +) { + emitter.on(key, handler as Handler); + if (immediate && latestRoute) { + handler(latestRoute); + } +} + +export function removeRouteListener() { + emitter.off(key); +} diff --git a/passiflora-ui/src/views/dashboard/workplace/components/announcement.vue b/passiflora-ui/src/views/dashboard/workplace/components/announcement.vue new file mode 100644 index 0000000..e1d1a13 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/announcement.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/banner.vue b/passiflora-ui/src/views/dashboard/workplace/components/banner.vue new file mode 100644 index 0000000..00b5534 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/banner.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/carousel.vue b/passiflora-ui/src/views/dashboard/workplace/components/carousel.vue new file mode 100644 index 0000000..2dbbbde --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/carousel.vue @@ -0,0 +1,24 @@ + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/categories-percent.vue b/passiflora-ui/src/views/dashboard/workplace/components/categories-percent.vue new file mode 100644 index 0000000..cbab4e3 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/categories-percent.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/content-chart.vue b/passiflora-ui/src/views/dashboard/workplace/components/content-chart.vue new file mode 100644 index 0000000..7189801 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/content-chart.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/data-panel.vue b/passiflora-ui/src/views/dashboard/workplace/components/data-panel.vue new file mode 100644 index 0000000..43d8999 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/data-panel.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/docs.vue b/passiflora-ui/src/views/dashboard/workplace/components/docs.vue new file mode 100644 index 0000000..506fae2 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/docs.vue @@ -0,0 +1,42 @@ + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/popular-content.vue b/passiflora-ui/src/views/dashboard/workplace/components/popular-content.vue new file mode 100644 index 0000000..6822bcd --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/popular-content.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/quick-operation.vue b/passiflora-ui/src/views/dashboard/workplace/components/quick-operation.vue new file mode 100644 index 0000000..fad1a21 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/quick-operation.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/components/recently-visited.vue b/passiflora-ui/src/views/dashboard/workplace/components/recently-visited.vue new file mode 100644 index 0000000..805c4f7 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/components/recently-visited.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/index.vue b/passiflora-ui/src/views/dashboard/workplace/index.vue new file mode 100644 index 0000000..379e5ce --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/index.vue @@ -0,0 +1,148 @@ + + + + + + + + + diff --git a/passiflora-ui/src/views/dashboard/workplace/locale/en-US.ts b/passiflora-ui/src/views/dashboard/workplace/locale/en-US.ts new file mode 100644 index 0000000..5eef6ad --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/locale/en-US.ts @@ -0,0 +1,38 @@ +export default { + 'menu.dashboard.workplace': 'Workplace', + 'workplace.welcome': 'Welcome!', + 'workplace.balance': 'Balance (CNY)', + 'workplace.order.pending': 'Pending', + 'workplace.order.pendingRenewal': 'Renewal Order', + 'workplace.onlineContent': 'Online Content', + 'workplace.putIn': 'Put In', + 'workplace.newDay': 'Daily Additional Comments', + 'workplace.newFromYesterday': 'New From Yesterday', + 'workplace.minute': 'Min', + 'workplace.docs': 'Documents', + 'workplace.docs.productOverview': 'Product Overview', + 'workplace.docs.userGuide': 'User Guide', + 'workplace.docs.workflow': 'Workflow', + 'workplace.docs.interfaceDocs': 'Interface Docs', + // + 'workplace.contentManagement': 'Content Management', + 'workplace.contentStatistical': 'Content Statistical', + 'workplace.advanced': 'Advanced', + 'workplace.onlinePromotion': 'Online Promotion', + 'workplace.contentPutIn': 'Put In', + 'workplace.announcement': 'Announcement', + 'workplace.recently.visited': 'Recently Visited', + 'workplace.record.nodata': 'No data', + 'workplace.quick.operation': 'Quick Operation', + 'workplace.quickOperation.setup': 'Setup', + 'workplace.allProject': 'All', + 'workplace.loadMore': 'More', + 'workplace.viewMore': 'More', + 'workplace.contentData': 'Content Data', + 'workplace.popularContent': 'Popular Content', + 'workplace.popularContent.text': 'text', + 'workplace.popularContent.image': 'image', + 'workplace.popularContent.video': 'video', + 'workplace.categoriesPercent': 'Categories Percent', + 'workplace.pecs': 'pecs', +}; diff --git a/passiflora-ui/src/views/dashboard/workplace/locale/zh-CN.ts b/passiflora-ui/src/views/dashboard/workplace/locale/zh-CN.ts new file mode 100644 index 0000000..4eebcc8 --- /dev/null +++ b/passiflora-ui/src/views/dashboard/workplace/locale/zh-CN.ts @@ -0,0 +1,37 @@ +export default { + 'menu.dashboard.workplace': '工作台', + 'workplace.welcome': '欢迎回来!', + 'workplace.balance': '余额(元)', + 'workplace.order.pending': '待支付', + 'workplace.order.pendingRenewal': '待续费订单', + 'workplace.onlineContent': '线上总内容', + 'workplace.putIn': '投放中内容', + 'workplace.newDay': '日新增评论', + 'workplace.newFromYesterday': '较昨日新增', + 'workplace.minute': '分钟', + 'workplace.docs': '帮助文档', + 'workplace.docs.productOverview': '产品概要', + 'workplace.docs.userGuide': '使用指南', + 'workplace.docs.workflow': '接入流程', + 'workplace.docs.interfaceDocs': '接口文档', + 'workplace.contentManagement': '内容管理', + 'workplace.contentStatistical': '内容分析', + 'workplace.advanced': '高级管理', + 'workplace.onlinePromotion': '线上推广', + 'workplace.contentPutIn': '内容投放', + 'workplace.announcement': '公告', + 'workplace.recently.visited': '最近访问', + 'workplace.record.nodata': '暂无数据', + 'workplace.quick.operation': '快捷操作', + 'workplace.quickOperation.setup': '管理', + 'workplace.allProject': '所有项目', + 'workplace.loadMore': '加载更多', + 'workplace.viewMore': '查看更多', + 'workplace.contentData': '内容数据', + 'workplace.popularContent': '线上热门内容', + 'workplace.popularContent.text': '文本', + 'workplace.popularContent.image': '图片', + 'workplace.popularContent.video': '视频', + 'workplace.categoriesPercent': '内容类型占比', + 'workplace.pecs': '个', +}; diff --git a/passiflora-ui/src/views/login/components/banner.vue b/passiflora-ui/src/views/login/components/banner.vue new file mode 100644 index 0000000..aabb110 --- /dev/null +++ b/passiflora-ui/src/views/login/components/banner.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/passiflora-ui/src/views/login/components/login-form.vue b/passiflora-ui/src/views/login/components/login-form.vue new file mode 100644 index 0000000..c860b0b --- /dev/null +++ b/passiflora-ui/src/views/login/components/login-form.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/passiflora-ui/src/views/login/index.vue b/passiflora-ui/src/views/login/index.vue new file mode 100644 index 0000000..7df0d1a --- /dev/null +++ b/passiflora-ui/src/views/login/index.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/passiflora-ui/src/views/login/locale/en-US.ts b/passiflora-ui/src/views/login/locale/en-US.ts new file mode 100644 index 0000000..9863e7f --- /dev/null +++ b/passiflora-ui/src/views/login/locale/en-US.ts @@ -0,0 +1,21 @@ +export default { + 'login.form.title': 'Login to Passiflora', + 'login.form.userName.errMsg': 'Username cannot be empty', + 'login.form.password.errMsg': 'Password cannot be empty', + 'login.form.login.errMsg': 'Login error, refresh and try again', + 'login.form.login.success': 'Login success', + 'login.form.userName.placeholder': 'Username: admin', + 'login.form.password.placeholder': 'Password: admin', + 'login.form.rememberPassword': 'Remember password', + 'login.form.forgetPassword': 'Forgot password', + 'login.form.login': 'login', + 'login.form.register': 'register account', + 'login.banner.slogan1': 'Out-of-the-box high-quality template', + 'login.banner.subSlogan1': + 'Rich page templates, covering most typical business scenarios', + 'login.banner.slogan2': 'Built-in solutions to common problems', + 'login.banner.subSlogan2': + 'Internationalization, routing configuration, state management everything', + 'login.banner.slogan3': 'Access visualization enhancement tool AUX', + 'login.banner.subSlogan3': 'Realize flexible block development', +}; diff --git a/passiflora-ui/src/views/login/locale/zh-CN.ts b/passiflora-ui/src/views/login/locale/zh-CN.ts new file mode 100644 index 0000000..8c053a4 --- /dev/null +++ b/passiflora-ui/src/views/login/locale/zh-CN.ts @@ -0,0 +1,19 @@ +export default { + 'login.form.title': '登录 Passiflora', + 'login.form.userName.errMsg': '用户名不能为空', + 'login.form.password.errMsg': '密码不能为空', + 'login.form.login.errMsg': '登录出错,轻刷新重试', + 'login.form.login.success': '登录成功', + 'login.form.userName.placeholder': '用户名:admin', + 'login.form.password.placeholder': '密码:admin', + 'login.form.rememberPassword': '记住密码', + 'login.form.forgetPassword': '忘记密码', + 'login.form.login': '登录', + 'login.form.register': '注册账号', + 'login.banner.slogan1': '开箱即用的高质量模板', + 'login.banner.subSlogan1': '丰富的的页面模板,覆盖大多数典型业务场景', + 'login.banner.slogan2': '内置了常见问题的解决方案', + 'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有', + 'login.banner.slogan3': '接入可视化增强工具AUX', + 'login.banner.subSlogan3': '实现灵活的区块式开发', +}; diff --git a/passiflora-ui/src/views/not-auth/index.vue b/passiflora-ui/src/views/not-auth/index.vue new file mode 100644 index 0000000..a110c78 --- /dev/null +++ b/passiflora-ui/src/views/not-auth/index.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/passiflora-ui/src/views/not-found/index.vue b/passiflora-ui/src/views/not-found/index.vue new file mode 100644 index 0000000..5aa32ce --- /dev/null +++ b/passiflora-ui/src/views/not-found/index.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/passiflora-ui/src/views/not-permission/index.vue b/passiflora-ui/src/views/not-permission/index.vue new file mode 100644 index 0000000..42c3ebd --- /dev/null +++ b/passiflora-ui/src/views/not-permission/index.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/passiflora-ui/src/views/organization/org/index.vue b/passiflora-ui/src/views/organization/org/index.vue new file mode 100644 index 0000000..2e1fe75 --- /dev/null +++ b/passiflora-ui/src/views/organization/org/index.vue @@ -0,0 +1,714 @@ + + + + + + + diff --git a/passiflora-ui/src/views/organization/position/index.vue b/passiflora-ui/src/views/organization/position/index.vue new file mode 100644 index 0000000..89a0923 --- /dev/null +++ b/passiflora-ui/src/views/organization/position/index.vue @@ -0,0 +1,605 @@ + + + + + + + diff --git a/passiflora-ui/src/views/organization/user/index.vue b/passiflora-ui/src/views/organization/user/index.vue new file mode 100644 index 0000000..545433d --- /dev/null +++ b/passiflora-ui/src/views/organization/user/index.vue @@ -0,0 +1,816 @@ + + + + + + + diff --git a/passiflora-ui/src/views/redirect/index.vue b/passiflora-ui/src/views/redirect/index.vue new file mode 100644 index 0000000..59930af --- /dev/null +++ b/passiflora-ui/src/views/redirect/index.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/passiflora-ui/src/views/system/dict/component/detail/index.vue b/passiflora-ui/src/views/system/dict/component/detail/index.vue new file mode 100644 index 0000000..219a9b6 --- /dev/null +++ b/passiflora-ui/src/views/system/dict/component/detail/index.vue @@ -0,0 +1,493 @@ + + + + + diff --git a/passiflora-ui/src/views/system/dict/index.vue b/passiflora-ui/src/views/system/dict/index.vue new file mode 100644 index 0000000..966dda7 --- /dev/null +++ b/passiflora-ui/src/views/system/dict/index.vue @@ -0,0 +1,554 @@ + + + + + + + diff --git a/passiflora-ui/src/views/system/permission/index.vue b/passiflora-ui/src/views/system/permission/index.vue new file mode 100644 index 0000000..9889959 --- /dev/null +++ b/passiflora-ui/src/views/system/permission/index.vue @@ -0,0 +1,713 @@ + + + + + + + diff --git a/passiflora-ui/tailwind.config.js b/passiflora-ui/tailwind.config.js new file mode 100644 index 0000000..55d1fb9 --- /dev/null +++ b/passiflora-ui/tailwind.config.js @@ -0,0 +1,12 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/passiflora-ui/tsconfig.json b/passiflora-ui/tsconfig.json new file mode 100644 index 0000000..dad9be8 --- /dev/null +++ b/passiflora-ui/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "lib": ["es2020", "dom"], + "skipLibCheck": true + }, + "include": ["src/**/*", "src/**/*.vue"], + "exclude": ["node_modules"] +} diff --git a/passiflora-ui/yarn.lock b/passiflora-ui/yarn.lock new file mode 100644 index 0000000..7bfb001 --- /dev/null +++ b/passiflora-ui/yarn.lock @@ -0,0 +1,8728 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@antfu/utils@^0.7.2": + version "0.7.7" + resolved "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.7.tgz" + integrity sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg== + +"@arco-design/color@^0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@arco-design/color/-/color-0.4.0.tgz" + integrity sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g== + dependencies: + color "^3.1.3" + +"@arco-design/web-vue@^2.44.7": + version "2.55.1" + resolved "https://registry.npmjs.org/@arco-design/web-vue/-/web-vue-2.55.1.tgz" + integrity sha512-MI0mteI4B1+UAKAslCrV2zqRKHWwkjLjdOpOLAR36cAbYhtlj7Tel3yAjGHk5zsQ1ODZX60OFfRfl66pq6919A== + dependencies: + "@arco-design/color" "^0.4.0" + b-tween "^0.3.3" + b-validate "^1.4.4" + compute-scroll-into-view "^1.0.17" + dayjs "^1.10.3" + number-precision "^1.5.0" + resize-observer-polyfill "^1.5.1" + scroll-into-view-if-needed "^2.2.28" + +"@arco-plugins/vite-vue@^1.4.5": + version "1.4.5" + resolved "https://registry.npmjs.org/@arco-plugins/vite-vue/-/vite-vue-1.4.5.tgz" + integrity sha512-2pJ9mpZP9mRD7NGZwRsZTS9C/US5ilEBBUqxN5Qgnd3Td50u9apJVKAABCZjG2K2eHiyZg7Fd9XhgHJXVJJmsw== + dependencies: + "@babel/generator" "^7.12.11" + "@babel/helper-module-imports" "^7.12.5" + "@babel/parser" "^7.12.11" + "@babel/traverse" "^7.12.12" + "@babel/types" "^7.12.12" + "@types/node" "^16.11.10" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/compat-data@^7.23.5": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.19.6", "@babel/core@>=7.2.2": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz" + integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.4" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.24.4" + "@babel/parser" "^7.24.4" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.12.11", "@babel/generator@^7.24.1", "@babel/generator@^7.24.4": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz" + integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== + dependencies: + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.24.4": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz" + integrity sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== + dependencies: + "@babel/types" "^7.23.0" + +"@babel/helper-module-imports@^7.12.5": + version "7.24.3" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-imports@^7.22.15": + version "7.24.3" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-imports@~7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0": + version "7.24.0" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz" + integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== + +"@babel/helper-replace-supers@^7.24.1": + version "7.24.1" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz" + integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.24.1" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.24.4": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz" + integrity sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.12.11", "@babel/parser@^7.15.8", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz" + integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== + +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.24.1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz" + integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-typescript@^7.24.1": + version "7.24.1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz" + integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-typescript@^7.20.0": + version "7.24.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz" + integrity sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.4" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-typescript" "^7.24.1" + +"@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + +"@babel/traverse@^7.12.12", "@babel/traverse@^7.23.9", "@babel/traverse@^7.24.1": + version "7.24.1" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== + dependencies: + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.1" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.12.12", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.9", "@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@commitlint/cli@^17.1.2": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz" + integrity sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg== + dependencies: + "@commitlint/format" "^17.8.1" + "@commitlint/lint" "^17.8.1" + "@commitlint/load" "^17.8.1" + "@commitlint/read" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + lodash.isfunction "^3.0.9" + resolve-from "5.0.0" + resolve-global "1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@^17.1.0": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz" + integrity sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg== + dependencies: + conventional-changelog-conventionalcommits "^6.1.0" + +"@commitlint/config-validator@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz" + integrity sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA== + dependencies: + "@commitlint/types" "^17.8.1" + ajv "^8.11.0" + +"@commitlint/ensure@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz" + integrity sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow== + dependencies: + "@commitlint/types" "^17.8.1" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz" + integrity sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ== + +"@commitlint/format@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz" + integrity sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg== + dependencies: + "@commitlint/types" "^17.8.1" + chalk "^4.1.0" + +"@commitlint/is-ignored@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz" + integrity sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g== + dependencies: + "@commitlint/types" "^17.8.1" + semver "7.5.4" + +"@commitlint/lint@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz" + integrity sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA== + dependencies: + "@commitlint/is-ignored" "^17.8.1" + "@commitlint/parse" "^17.8.1" + "@commitlint/rules" "^17.8.1" + "@commitlint/types" "^17.8.1" + +"@commitlint/load@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz" + integrity sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA== + dependencies: + "@commitlint/config-validator" "^17.8.1" + "@commitlint/execute-rule" "^17.8.1" + "@commitlint/resolve-extends" "^17.8.1" + "@commitlint/types" "^17.8.1" + "@types/node" "20.5.1" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + cosmiconfig-typescript-loader "^4.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + resolve-from "^5.0.0" + ts-node "^10.8.1" + typescript "^4.6.4 || ^5.2.2" + +"@commitlint/message@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz" + integrity sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA== + +"@commitlint/parse@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz" + integrity sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw== + dependencies: + "@commitlint/types" "^17.8.1" + conventional-changelog-angular "^6.0.0" + conventional-commits-parser "^4.0.0" + +"@commitlint/read@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz" + integrity sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w== + dependencies: + "@commitlint/top-level" "^17.8.1" + "@commitlint/types" "^17.8.1" + fs-extra "^11.0.0" + git-raw-commits "^2.0.11" + minimist "^1.2.6" + +"@commitlint/resolve-extends@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz" + integrity sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q== + dependencies: + "@commitlint/config-validator" "^17.8.1" + "@commitlint/types" "^17.8.1" + import-fresh "^3.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + resolve-global "^1.0.0" + +"@commitlint/rules@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz" + integrity sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA== + dependencies: + "@commitlint/ensure" "^17.8.1" + "@commitlint/message" "^17.8.1" + "@commitlint/to-lines" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + +"@commitlint/to-lines@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz" + integrity sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA== + +"@commitlint/top-level@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz" + integrity sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA== + dependencies: + find-up "^5.0.0" + +"@commitlint/types@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz" + integrity sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ== + dependencies: + chalk "^4.1.0" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@csstools/selector-specificity@^2.0.2": + version "2.2.0" + resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz" + integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@intlify/core-base@9.13.1": + version "9.13.1" + resolved "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz" + integrity sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w== + dependencies: + "@intlify/message-compiler" "9.13.1" + "@intlify/shared" "9.13.1" + +"@intlify/message-compiler@9.13.1": + version "9.13.1" + resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz" + integrity sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w== + dependencies: + "@intlify/shared" "9.13.1" + source-map-js "^1.0.2" + +"@intlify/shared@9.13.1": + version "9.13.1" + resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.13.1.tgz" + integrity sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@rollup/pluginutils@^4.2.1": + version "4.2.1" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^5.0.2": + version "5.1.0" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz" + integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/eslint@^8.4.5": + version "8.56.10" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/imagemin-gifsicle@^7.0.1": + version "7.0.4" + resolved "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.4.tgz" + integrity sha512-ZghMBd/Jgqg5utTJNPmvf6DkuHzMhscJ8vgf/7MUGCpO+G+cLrhYltL+5d+h3A1B4W73S2SrmJZ1jS5LACpX+A== + dependencies: + "@types/imagemin" "*" + +"@types/imagemin-jpegtran@^5.0.1": + version "5.0.4" + resolved "https://registry.npmjs.org/@types/imagemin-jpegtran/-/imagemin-jpegtran-5.0.4.tgz" + integrity sha512-PSMxOeJa8q94Y+qx8Yriw+qj1+vH5xWpvar63o6SGO0Xi5RlKuwHHfJmN2GRUngPrlhe394jOUmpVq8jQlVmFA== + dependencies: + "@types/imagemin" "*" + +"@types/imagemin-mozjpeg@^8.0.1": + version "8.0.4" + resolved "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.4.tgz" + integrity sha512-ZCAxV8SYJB8ehwHpnbRpHjg5Wc4HcyuAMiDhXbkgC7gujDoOTyHO3dhDkUtZ1oK1DLBRZapqG9etdLVhUml7yQ== + dependencies: + "@types/imagemin" "*" + +"@types/imagemin-optipng@^5.2.1": + version "5.2.4" + resolved "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.4.tgz" + integrity sha512-mvKnDMC8eCYZetAQudjs1DbgpR84WhsTx1wgvdiXnpuUEti3oJ+MaMYBRWPY0JlQ4+y4TXKOfa7+LOuT8daegQ== + dependencies: + "@types/imagemin" "*" + +"@types/imagemin-svgo@^10.0.0": + version "10.0.5" + resolved "https://registry.npmjs.org/@types/imagemin-svgo/-/imagemin-svgo-10.0.5.tgz" + integrity sha512-9U2Rf7vWBHeqJvzmWNP3vYAKqR0208QqQ9Mkrq9OLIL5AeoF/dRVRou6iUYCufBSim57BpBpCJhZLrTgfS3k1g== + dependencies: + "@types/imagemin" "*" + "@types/svgo" "2" + +"@types/imagemin-webp@^7.0.0": + version "7.0.3" + resolved "https://registry.npmjs.org/@types/imagemin-webp/-/imagemin-webp-7.0.3.tgz" + integrity sha512-C2/EMohS4bzsvY5VJvdzHFdcfmnZoui54DmM/9bFtK57/CgGmKkc+p6n49euPGmMFDDvwm4yVl60nwxcZOmH5A== + dependencies: + "@types/imagemin" "*" + +"@types/imagemin@*": + version "8.0.5" + resolved "https://registry.npmjs.org/@types/imagemin/-/imagemin-8.0.5.tgz" + integrity sha512-tah3dm+5sG+fEDAz6CrQ5evuEaPX9K6DF3E5a01MPOKhA2oGBoC+oA5EJzSugB905sN4DE19EDzldT2Cld2g6Q== + dependencies: + "@types/node" "*" + +"@types/imagemin@^7.0.1": + version "7.0.1" + resolved "https://registry.npmjs.org/@types/imagemin/-/imagemin-7.0.1.tgz" + integrity sha512-xEn5+M3lDBtI3JxLy6eU3ksoVurygnlG7OYhTqJfGGP4PcvYnfn+IABCmMve7ziM/SneHDm5xgJFKC8hCYPicw== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/lodash@^4.14.186": + version "4.17.0" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz" + integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/minimist@^1.2.0": + version "1.2.5" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== + +"@types/node@*", "@types/node@>= 14": + version "20.12.7" + resolved "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^16.11.10": + version "16.18.96" + resolved "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz" + integrity sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ== + +"@types/node@20.5.1": + version "20.5.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz" + integrity sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg== + +"@types/normalize-package-data@^2.4.0": + version "2.4.4" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + +"@types/nprogress@^0.2.0": + version "0.2.3" + resolved "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.3.tgz" + integrity sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA== + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/sortablejs@^1.15.0": + version "1.15.8" + resolved "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz" + integrity sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg== + +"@types/svgo@^2.6.1", "@types/svgo@2": + version "2.6.4" + resolved "https://registry.npmjs.org/@types/svgo/-/svgo-2.6.4.tgz" + integrity sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng== + dependencies: + "@types/node" "*" + +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz" + integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== + +"@types/unist@^2.0.0": + version "2.0.10" + resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + +"@types/vfile-message@*": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-2.0.0.tgz" + integrity sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw== + dependencies: + vfile-message "*" + +"@types/vfile@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz" + integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw== + dependencies: + "@types/node" "*" + "@types/unist" "*" + "@types/vfile-message" "*" + +"@types/web-bluetooth@^0.0.16": + version "0.0.16" + resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz" + integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ== + +"@typescript-eslint/eslint-plugin@^5.40.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.40.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vitejs/plugin-vue-jsx@^2.0.1": + version "2.1.1" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-2.1.1.tgz" + integrity sha512-JgDhxstQlwnHBvZ1BSnU5mbmyQ14/t5JhREc6YH5kWyu2QdAAOsLF6xgHoIWarj8tddaiwFrNzLbWJPudpXKYA== + dependencies: + "@babel/core" "^7.19.6" + "@babel/plugin-transform-typescript" "^7.20.0" + "@vue/babel-plugin-jsx" "^1.1.1" + +"@vitejs/plugin-vue@^3.1.2": + version "3.2.0" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz" + integrity sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw== + +"@volar/language-core@~1.11.1", "@volar/language-core@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz" + integrity sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw== + dependencies: + "@volar/source-map" "1.11.1" + +"@volar/source-map@~1.11.1", "@volar/source-map@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz" + integrity sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg== + dependencies: + muggle-string "^0.3.1" + +"@volar/typescript@~1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz" + integrity sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ== + dependencies: + "@volar/language-core" "1.11.1" + path-browserify "^1.0.1" + +"@vue/babel-helper-vue-transform-on@1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.2.2.tgz" + integrity sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw== + +"@vue/babel-plugin-jsx@^1.1.1": + version "1.2.2" + resolved "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.2.2.tgz" + integrity sha512-nYTkZUVTu4nhP199UoORePsql0l+wj7v/oyQjtThUVhJl1U+6qHuoVhIvR3bf7eVKjbCK+Cs2AWd7mi9Mpz9rA== + dependencies: + "@babel/helper-module-imports" "~7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + "@vue/babel-helper-vue-transform-on" "1.2.2" + "@vue/babel-plugin-resolve-type" "1.2.2" + camelcase "^6.3.0" + html-tags "^3.3.1" + svg-tags "^1.0.0" + +"@vue/babel-plugin-resolve-type@1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.2.2.tgz" + integrity sha512-EntyroPwNg5IPVdUJupqs0CFzuf6lUrVvCspmv2J1FITLeGnUCuoGNNk78dgCusxEiYj6RMkTJflGSxk5aIC4A== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/helper-module-imports" "~7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/parser" "^7.23.9" + "@vue/compiler-sfc" "^3.4.15" + +"@vue/compiler-core@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz" + integrity sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A== + dependencies: + "@babel/parser" "^7.24.4" + "@vue/shared" "3.4.24" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" + +"@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz" + integrity sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA== + dependencies: + "@vue/compiler-core" "3.4.24" + "@vue/shared" "3.4.24" + +"@vue/compiler-sfc@^3.2.20", "@vue/compiler-sfc@^3.4.15", "@vue/compiler-sfc@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz" + integrity sha512-nRAlJUK02FTWfA2nuvNBAqsDZuERGFgxZ8sGH62XgFSvMxO2URblzulExsmj4gFZ8e+VAyDooU9oAoXfEDNxTA== + dependencies: + "@babel/parser" "^7.24.4" + "@vue/compiler-core" "3.4.24" + "@vue/compiler-dom" "3.4.24" + "@vue/compiler-ssr" "3.4.24" + "@vue/shared" "3.4.24" + estree-walker "^2.0.2" + magic-string "^0.30.10" + postcss "^8.4.38" + source-map-js "^1.2.0" + +"@vue/compiler-ssr@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz" + integrity sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ== + dependencies: + "@vue/compiler-dom" "3.4.24" + "@vue/shared" "3.4.24" + +"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.5.1": + version "6.6.1" + resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz" + integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA== + +"@vue/language-core@1.8.27": + version "1.8.27" + resolved "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz" + integrity sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA== + dependencies: + "@volar/language-core" "~1.11.1" + "@volar/source-map" "~1.11.1" + "@vue/compiler-dom" "^3.3.0" + "@vue/shared" "^3.3.0" + computeds "^0.0.1" + minimatch "^9.0.3" + muggle-string "^0.3.1" + path-browserify "^1.0.1" + vue-template-compiler "^2.7.14" + +"@vue/reactivity@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.24.tgz" + integrity sha512-nup3fSYg4i4LtNvu9slF/HF/0dkMQYfepUdORBcMSsankzRPzE7ypAFurpwyRBfU1i7Dn1kcwpYsE1wETSh91g== + dependencies: + "@vue/shared" "3.4.24" + +"@vue/runtime-core@^3.0.0", "@vue/runtime-core@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.24.tgz" + integrity sha512-c7iMfj6cJMeAG3s5yOn9Rc5D9e2/wIuaozmGf/ICGCY3KV5H7mbTVdvEkd4ZshTq7RUZqj2k7LMJWVx+EBiY1g== + dependencies: + "@vue/reactivity" "3.4.24" + "@vue/shared" "3.4.24" + +"@vue/runtime-dom@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.24.tgz" + integrity sha512-uXKzuh/Emfad2Y7Qm0ABsLZZV6H3mAJ5ZVqmAOlrNQRf+T5mxpPGZBfec1hkP41t6h6FwF6RSGCs/gd8WbuySQ== + dependencies: + "@vue/runtime-core" "3.4.24" + "@vue/shared" "3.4.24" + csstype "^3.1.3" + +"@vue/server-renderer@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.24.tgz" + integrity sha512-H+DLK4sQF6sRgzKyofmlEVBIV/9KrQU6HIV7nt6yIwSGGKvSwlV8pqJlebUKLpbXaNHugdSfAbP6YmXF69lxow== + dependencies: + "@vue/compiler-ssr" "3.4.24" + "@vue/shared" "3.4.24" + +"@vue/shared@^3.3.0", "@vue/shared@3.4.24": + version "3.4.24" + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz" + integrity sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw== + +"@vueuse/core@^9.3.0": + version "9.13.0" + resolved "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz" + integrity sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw== + dependencies: + "@types/web-bluetooth" "^0.0.16" + "@vueuse/metadata" "9.13.0" + "@vueuse/shared" "9.13.0" + vue-demi "*" + +"@vueuse/metadata@9.13.0": + version "9.13.0" + resolved "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz" + integrity sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ== + +"@vueuse/shared@9.13.0": + version "9.13.0" + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz" + integrity sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw== + dependencies: + vue-demi "*" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.3.2" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.11.3, acorn@^8.4.1, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv@^6.10.2, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^8.11.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.0.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arch@^2.1.0: + version "2.2.0" + resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +archive-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz" + integrity sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA== + dependencies: + file-type "^4.2.0" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + +array-includes@^3.1.7: + version "3.1.8" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +array.prototype.findlastindex@^1.2.3: + version "1.2.5" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^10.4.19: + version "10.4.19" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz" + integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== + dependencies: + browserslist "^4.23.0" + caniuse-lite "^1.0.30001599" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +autoprefixer@^9.0.0: + version "9.8.8" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz" + integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + picocolors "^0.2.1" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + +b-tween@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/b-tween/-/b-tween-0.3.3.tgz" + integrity sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA== + +b-validate@^1.4.4: + version "1.5.3" + resolved "https://registry.npmjs.org/b-validate/-/b-validate-1.5.3.tgz" + integrity sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA== + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bin-build@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz" + integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== + dependencies: + decompress "^4.0.0" + download "^6.2.2" + execa "^0.7.0" + p-map-series "^1.0.0" + tempfile "^2.0.0" + +bin-check@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz" + integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== + dependencies: + execa "^0.7.0" + executable "^4.1.0" + +bin-version-check@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz" + integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== + dependencies: + bin-version "^3.0.0" + semver "^5.6.0" + semver-truncate "^1.1.2" + +bin-version@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz" + integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== + dependencies: + execa "^1.0.0" + find-versions "^3.0.0" + +bin-wrapper@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz" + integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== + dependencies: + bin-check "^4.1.0" + bin-version-check "^4.0.0" + download "^7.1.0" + import-lazy "^3.1.0" + os-filter-obj "^2.0.0" + pify "^4.0.1" + +bin-wrapper@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz" + integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== + dependencies: + bin-check "^4.1.0" + bin-version-check "^4.0.0" + download "^7.1.0" + import-lazy "^3.1.0" + os-filter-obj "^2.0.0" + pify "^4.0.1" + +"bin-wrapper@npm:bin-wrapper-china": + version "0.1.0" + resolved "https://registry.npmjs.org/bin-wrapper-china/-/bin-wrapper-china-0.1.0.tgz" + integrity sha512-1UCm17WYEbgry50tup+AQN+JGVEVzoW4f8HMl899k1lvuFxWKGZXl/G2fgxQxAckRjnloO3ijLVVEsv8zescUg== + dependencies: + bin-check "^4.1.0" + bin-version-check "^4.0.0" + binary-mirror-config "^1" + download "^7.1.0" + import-lazy "^4.0.0" + os-filter-obj "^2.0.0" + pify "^4.0.1" + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +binary-mirror-config@^1: + version "1.41.0" + resolved "https://registry.npmjs.org/binary-mirror-config/-/binary-mirror-config-1.41.0.tgz" + integrity sha512-ZiIhR1s6Sv1Fv6qCQqfPjx0Cj86BgFlhqNxZgHkQOWcxJcMbO3mj1iqsuVjowYqJqeZL8e52+IEv7IRnSX6T6w== + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.12.0, browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4.21.0": + version "4.23.0" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz" + integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== + +buffer@^5.2.1: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz" + integrity sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ== + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" + integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" + integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" + integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" + integrity sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ== + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz" + integrity sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q== + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" + integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz" + integrity sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: + version "1.0.30001612" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz" + integrity sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g== + +caw@^2.0.0, caw@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz" + integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== + dependencies: + get-proxy "^2.0.0" + isurl "^1.0.0-alpha5" + tunnel-agent "^0.6.0" + url-to-options "^1.0.1" + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.1: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +character-entities-html4@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz" + integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +chokidar@^3.5.3, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-regexp@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz" + integrity sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw== + dependencies: + is-regexp "^1.0.0" + is-supported-regexp-flag "^1.0.0" + +clone-response@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" + integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== + dependencies: + mimic-response "^1.0.0" + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^2.8.1: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz" + integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== + +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + +component-emitter@^1.2.1: + version "1.3.1" + resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + +compute-scroll-into-view@^1.0.17, compute-scroll-into-view@^1.0.20: + version "1.0.20" + resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz" + integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg== + +computeds@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz" + integrity sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +confusing-browser-globals@^1.0.10: + version "1.0.11" + resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +console-stream@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz" + integrity sha512-QC/8l9e6ofi6nqZ5PawlDgzmMw3OxIXtvolBzap/F4UDBJlDaZRSNbL/lb41C29FcbSJncBFlJFj2WJoNyZRfQ== + +content-disposition@^0.5.2: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +conventional-changelog-angular@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz" + integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== + dependencies: + compare-func "^2.0.0" + +conventional-changelog-conventionalcommits@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz" + integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== + dependencies: + compare-func "^2.0.0" + +conventional-commits-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz" + integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== + dependencies: + is-text-path "^1.0.1" + JSONStream "^1.3.5" + meow "^8.1.2" + split2 "^3.2.2" + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +copy-anything@^2.0.1: + version "2.0.6" + resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz" + integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw== + dependencies: + is-what "^3.14.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig-typescript-loader@^4.0.0: + version "4.4.0" + resolved "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz" + integrity sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw== + +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cosmiconfig@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cosmiconfig@^8.0.0, cosmiconfig@>=7: + version "8.3.6" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-functions-list@^3.1.0: + version "3.2.2" + resolved "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz" + integrity sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ== + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" + integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== + dependencies: + array-find-index "^1.0.1" + +cwebp-bin@^6.0.0: + version "6.1.2" + resolved "https://registry.npmjs.org/cwebp-bin/-/cwebp-bin-6.1.2.tgz" + integrity sha512-NLEZ/BVAl9g426hwUX/qrQ7b/EfQH7BS1tr+CzPo2EgDQbcdzmUVE+fIfsi64lsL638lWgzTEViMAL4pxV1GOg== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.1" + +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +dayjs@^1.10.3, dayjs@^1.11.5: + version "1.11.10" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz" + integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^2.3.3: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.0, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +decode-uri-component@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz" + integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz" + integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw== + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0, decompress@^4.2.0: + version "4.2.1" + resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + +deep-is@^0.1.3: + 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== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domelementtype@^1.3.1, domelementtype@1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.2: + version "5.0.3" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-prop@^5.1.0, dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +download@^6.2.2: + version "6.2.5" + resolved "https://registry.npmjs.org/download/-/download-6.2.5.tgz" + integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== + dependencies: + caw "^2.0.0" + content-disposition "^0.5.2" + decompress "^4.0.0" + ext-name "^5.0.0" + file-type "5.2.0" + filenamify "^2.0.0" + get-stream "^3.0.0" + got "^7.0.0" + make-dir "^1.0.0" + p-event "^1.0.0" + pify "^3.0.0" + +download@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/download/-/download-7.1.0.tgz" + integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== + dependencies: + archive-type "^4.0.0" + caw "^2.0.1" + content-disposition "^0.5.2" + decompress "^4.2.0" + ext-name "^5.0.0" + file-type "^8.1.0" + filenamify "^2.0.0" + get-stream "^3.0.0" + got "^8.3.1" + make-dir "^1.2.0" + p-event "^2.1.0" + pify "^3.0.0" + +duplexer3@^0.1.4: + version "0.1.5" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +echarts@^5.4.0, echarts@^5.4.1: + version "5.5.0" + resolved "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz" + integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== + dependencies: + tslib "2.3.0" + zrender "5.5.0" + +electron-to-chromium@^1.4.668: + version "1.4.746" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.746.tgz" + integrity sha512-jeWaIta2rIG2FzHaYIhSuVWqC6KJYo7oSBX4Jv7g+aVujKztfvdpf+n6MGwZdC5hQXbax4nntykLH2juIQrfPg== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.12.0: + version "5.16.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz" + integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +errno@^0.1.1: + version "0.1.8" + resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-darwin-arm64@0.15.18: + version "0.15.18" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz" + integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA== + +esbuild@^0.14.14: + version "0.14.54" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== + optionalDependencies: + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" + +esbuild@^0.15.9: + version "0.15.18" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz" + integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q== + optionalDependencies: + "@esbuild/android-arm" "0.15.18" + "@esbuild/linux-loong64" "0.15.18" + esbuild-android-64 "0.15.18" + esbuild-android-arm64 "0.15.18" + esbuild-darwin-64 "0.15.18" + esbuild-darwin-arm64 "0.15.18" + esbuild-freebsd-64 "0.15.18" + esbuild-freebsd-arm64 "0.15.18" + esbuild-linux-32 "0.15.18" + esbuild-linux-64 "0.15.18" + esbuild-linux-arm "0.15.18" + esbuild-linux-arm64 "0.15.18" + esbuild-linux-mips64le "0.15.18" + esbuild-linux-ppc64le "0.15.18" + esbuild-linux-riscv64 "0.15.18" + esbuild-linux-s390x "0.15.18" + esbuild-netbsd-64 "0.15.18" + esbuild-openbsd-64 "0.15.18" + esbuild-sunos-64 "0.15.18" + esbuild-windows-32 "0.15.18" + esbuild-windows-64 "0.15.18" + esbuild-windows-arm64 "0.15.18" + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.5" + semver "^6.3.0" + +eslint-config-prettier@^8.5.0: + version "8.10.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.5.1: + version "3.6.1" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" + is-glob "^4.0.3" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: + version "2.8.1" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@*, eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.26.0: + version "2.29.1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-vue@^9.6.0: + version "9.25.0" + resolved "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz" + integrity sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + globals "^13.24.0" + natural-compare "^1.4.0" + nth-check "^2.1.1" + postcss-selector-parser "^6.0.15" + semver "^7.6.0" + vue-eslint-parser "^9.4.2" + xml-name-validator "^4.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1, eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + 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== + +eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "eslint@^7.32.0 || ^8.2.0", eslint@^8.57.0, eslint@>=6.0.0, eslint@>=7, eslint@>=7.0.0, eslint@>=7.28.0: + version "8.57.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.3.1, espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0, esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +exec-buffer@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz" + integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== + dependencies: + execa "^0.7.0" + p-finally "^1.0.0" + pify "^3.0.0" + rimraf "^2.5.4" + tempfile "^2.0.0" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz" + integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw== + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +execall@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execall/-/execall-1.0.0.tgz" + integrity sha512-/J0Q8CvOvlAdpvhfkD/WnTQ4H1eU0exze2nFGPj/RSC7jpQ0NkKe2r28T5eMkhEEs+fzepMZNy1kVRKNlC04nQ== + dependencies: + clone-regexp "^1.0.0" + +executable@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +ext-list@^2.0.0: + version "2.2.2" + resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz" + integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== + dependencies: + mime-db "^1.28.0" + +ext-name@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz" + integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== + dependencies: + ext-list "^2.0.0" + sort-keys-length "^1.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + 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== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.0.3, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + 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== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-xml-parser@^4.1.3: + version "4.3.6" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz" + integrity sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw== + dependencies: + strnum "^1.0.5" + +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" + integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-entry-cache@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-4.0.0.tgz" + integrity sha512-AVSwsnbV8vH/UVbvgEhf3saVQXORNv0ZzSkvkhQIaia5Tia+JhGTaa/ePUSVoPHQyGayQNmYfkzFi3WZV5zcpA== + dependencies: + flat-cache "^2.0.1" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-type@^10.4.0: + version "10.11.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz" + integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== + +file-type@^10.5.0: + version "10.11.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz" + integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== + +file-type@^12.0.0: + version "12.4.2" + resolved "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz" + integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg== + +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" + integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== + +file-type@^4.2.0: + version "4.4.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz" + integrity sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ== + +file-type@^5.2.0, file-type@5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz" + integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ== + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +file-type@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz" + integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== + +filename-reserved-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz" + integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== + +filenamify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz" + integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== + dependencies: + filename-reserved-regex "^2.0.0" + strip-outer "^1.0.0" + trim-repeated "^1.0.0" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +filter-obj@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz" + integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng== + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" + integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-versions@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz" + integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== + dependencies: + semver-regex "^2.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.14.4: + version "1.15.6" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz" + integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.0.0: + version "11.2.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-proxy@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz" + integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== + dependencies: + npm-conf "^1.1.0" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz" + integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA== + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +get-stream@^3.0.0, get-stream@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" + integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +get-tsconfig@^4.5.0: + version "4.7.3" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz" + integrity sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg== + dependencies: + resolve-pkg-maps "^1.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +gifsicle@^5.0.0, gifsicle@5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/gifsicle/-/gifsicle-5.2.0.tgz" + integrity sha512-vOIS3j0XoTCxq9pkGj43gEix82RkI5FveNgaFZutjbaui/HH+4fR8Y56dwXDuxYo8hR4xOo6/j2h1WHoQW6XLw== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + execa "^5.0.0" + logalot "^2.0.0" + +git-raw-commits@^2.0.11: + version "2.0.11" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz" + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== + +glob@^10.3.10: + version "10.3.12" + resolved "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz" + integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== + dependencies: + ini "^1.3.4" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0, globals@^13.24.0: + version "13.24.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^10.0.0: + version "10.0.2" + resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^9.0.0: + version "9.2.0" + resolved "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz" + integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== + +gonzales-pe@^4.2.3: + version "4.3.0" + resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +got@^8.3.1: + version "8.3.2" + resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz" + integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.4.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" + integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== + dependencies: + ansi-regex "^2.0.0" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz" + integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g== + +html-tags@^3.2.0, html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +htmlparser2@^3.10.0: + version "3.10.1" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@^8.0.0: + version "8.0.2" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.1: + version "5.3.1" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" + integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== + +imagemin-gifsicle@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz" + integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA== + dependencies: + execa "^1.0.0" + gifsicle "^5.0.0" + is-gif "^3.0.0" + +imagemin-jpegtran@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-7.0.0.tgz" + integrity sha512-MJoyTCW8YjMJf56NorFE41SR/WkaGA3IYk4JgvMlRwguJEEd3PnP9UxA8Y2UWjquz8d+On3Ds/03ZfiiLS8xTQ== + dependencies: + exec-buffer "^3.0.0" + is-jpg "^2.0.0" + jpegtran-bin "^5.0.0" + +imagemin-mozjpeg@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/imagemin-mozjpeg/-/imagemin-mozjpeg-9.0.0.tgz" + integrity sha512-TwOjTzYqCFRgROTWpVSt5UTT0JeCuzF1jswPLKALDd89+PmrJ2PdMMYeDLYZ1fs9cTovI9GJd68mRSnuVt691w== + dependencies: + execa "^4.0.0" + is-jpg "^2.0.0" + mozjpeg "^7.0.0" + +imagemin-optipng@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-8.0.0.tgz" + integrity sha512-CUGfhfwqlPjAC0rm8Fy+R2DJDBGjzy2SkfyT09L8rasnF9jSoHFqJ1xxSZWK6HVPZBMhGPMxCTL70OgTHlLF5A== + dependencies: + exec-buffer "^3.0.0" + is-png "^2.0.0" + optipng-bin "^7.0.0" + +imagemin-pngquant@^9.0.2: + version "9.0.2" + resolved "https://registry.npmjs.org/imagemin-pngquant/-/imagemin-pngquant-9.0.2.tgz" + integrity sha512-cj//bKo8+Frd/DM8l6Pg9pws1pnDUjgb7ae++sUX1kUVdv2nrngPykhiUOgFeE0LGY/LmUbCf4egCHC4YUcZSg== + dependencies: + execa "^4.0.0" + is-png "^2.0.0" + is-stream "^2.0.0" + ow "^0.17.0" + pngquant-bin "^6.0.0" + +imagemin-svgo@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-9.0.0.tgz" + integrity sha512-uNgXpKHd99C0WODkrJ8OO/3zW3qjgS4pW7hcuII0RcHN3tnKxDjJWcitdVC/TZyfIqSricU8WfrHn26bdSW62g== + dependencies: + is-svg "^4.2.1" + svgo "^2.1.0" + +imagemin-webp@^6.0.0: + version "6.1.0" + resolved "https://registry.npmjs.org/imagemin-webp/-/imagemin-webp-6.1.0.tgz" + integrity sha512-i8ZluZV1pfQX9aVzmZ/VZh9KBSdPwUlp5VruAa9c30GZnX/nMl5n7h+oUMnI7Mg7+SUpu9mYBsw2nsYGUEllWQ== + dependencies: + cwebp-bin "^6.0.0" + exec-buffer "^3.0.0" + is-cwebp-readable "^3.0.0" + +imagemin@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz" + integrity sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w== + dependencies: + file-type "^12.0.0" + globby "^10.0.0" + graceful-fs "^4.2.2" + junk "^3.1.0" + make-dir "^3.0.0" + p-pipe "^3.0.0" + replace-ext "^1.0.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" + integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz" + integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== + +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" + integrity sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg== + dependencies: + repeating "^2.0.0" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz" + integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" + integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4, ini@^1.3.5: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz" + integrity sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ== + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + +is-accessor-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz" + integrity sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA== + dependencies: + hasown "^2.0.0" + +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumeric@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz" + integrity sha512-ZmRL7++ZkcMOfDuWZuMJyIVLr2keE1o/DeNWh1EmgqGhUcV+9BIVsx0BcSBOHTZqzjs4+dISzr2KAeBEWGgXeA== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0: + version "2.13.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-cwebp-readable@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-cwebp-readable/-/is-cwebp-readable-3.0.0.tgz" + integrity sha512-bpELc7/Q1/U5MWHn4NdHI44R3jxk0h9ew9ljzabiRl70/UIjL/ZAqRMb52F5+eke/VC8yTiv4Ewryo1fPWidvA== + dependencies: + file-type "^10.5.0" + +is-data-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz" + integrity sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-descriptor@^0.1.0: + version "0.1.7" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz" + integrity sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz" + integrity sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" + integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + 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== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-gif@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz" + integrity sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw== + dependencies: + file-type "^10.4.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz" + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-jpg@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz" + integrity sha512-ODlO0ruzhkzD3sdynIainVP5eoOFNN85rxA1+cwwnPe4dKyX0r5+hxNO5XpCrxlHcmb9vkOit9mhRD2JVuimHg== + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz" + integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-png@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz" + integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^1.0.0, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-supported-regexp-flag@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz" + integrity sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ== + +is-svg@^4.2.1: + version "4.4.0" + resolved "https://registry.npmjs.org/is-svg/-/is-svg-4.4.0.tgz" + integrity sha512-v+AgVwiK5DsGtT9ng+m4mClp6zDAmwrW8nZi6Gg15qzvBnRWWdfWA1TGaXyCDnWq5g5asofIgMVl3PjKxvk1ug== + dependencies: + fast-xml-parser "^4.1.3" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== + dependencies: + text-extensions "^1.0.0" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-what@^3.14.1: + version "3.14.1" + resolved "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0, isarray@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^1.21.0: + version "1.21.0" + resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +jpegtran-bin@^5.0.0: + version "5.0.2" + resolved "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-5.0.2.tgz" + integrity sha512-4FSmgIcr8d5+V6T1+dHbPZjaFH0ogVyP4UVsE+zri7S9YLO4qAT2our4IN3sW3STVgNTbqPermdIgt2XuAJ4EA== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + logalot "^2.0.0" + +jpegtran-bin@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-6.0.1.tgz" + integrity sha512-WohhhHhqe22de7PU8hXs6Sr5d4BAvkrfA93NR5tGlHyPnFLgvEW/bH+q7fv65JgoiQDsd7SBwwQ/OGRBivU3Mw== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^8.0.0: + version "8.0.3" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz" + integrity sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" + integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + 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== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + 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== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +junk@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz" + integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz" + integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +known-css-properties@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.11.0.tgz" + integrity sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w== + +known-css-properties@^0.26.0: + version "0.26.0" + resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz" + integrity sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg== + +less@*, less@^4.1.3: + version "4.2.0" + resolved "https://registry.npmjs.org/less/-/less-4.2.0.tgz" + integrity sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA== + dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" + tslib "^2.3.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + needle "^3.1.0" + source-map "~0.6.0" + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz" + integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^2.1.0, lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz" + integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@^13.0.3: + version "13.3.0" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz" + integrity sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ== + dependencies: + chalk "5.3.0" + commander "11.0.0" + debug "4.3.4" + execa "7.2.0" + lilconfig "2.1.0" + listr2 "6.6.1" + micromatch "4.0.5" + pidtree "0.6.0" + string-argv "0.3.2" + yaml "2.3.1" + +listr2@6.6.1: + version "6.6.1" + resolved "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz" + integrity sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^5.0.1" + rfdc "^1.3.0" + wrap-ansi "^8.1.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" + integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^2.0.0, log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +log-update@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz" + integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== + dependencies: + ansi-escapes "^5.0.0" + cli-cursor "^4.0.0" + slice-ansi "^5.0.0" + strip-ansi "^7.0.1" + wrap-ansi "^8.0.1" + +logalot@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz" + integrity sha512-Ah4CgdSRfeCJagxQhcVNMi9BfGYyEKLa6d7OA6xSbld/Hg3Cf2QiOa1mDpmG7Ve8LOH6DN3mdttzjQAvWTyVkw== + dependencies: + figures "^1.3.5" + squeak "^1.0.0" + +longest-streak@^2.0.1: + version "2.0.4" + resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz" + integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== + +longest@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" + integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz" + integrity sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ== + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" + integrity sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A== + +lpad-align@^1.0.1: + version "1.1.2" + resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz" + integrity sha512-MMIcFmmR9zlGZtBcFOows6c2COMekHCIFJz3ew/rRpKZ1wR4mXDPzvcVqLarux8M33X4TPSq2Jdw8WJj0q0KbQ== + dependencies: + get-stdin "^4.0.1" + indent-string "^2.1.0" + longest "^1.0.0" + meow "^3.3.0" + +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.30.0, magic-string@^0.30.10: + version "0.30.10" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz" + integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +make-dir@^1.0.0, make-dir@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz" + integrity sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +markdown-table@^1.1.0: + version "1.1.3" + resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz" + integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== + +mathml-tag-names@^2.0.1, mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + +mdast-util-compact@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz" + integrity sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg== + dependencies: + unist-util-visit "^1.1.0" + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" + integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz" + integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + +meow@^8.0.0, meow@^8.1.2: + version "8.1.2" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.4, micromatch@^4.0.5, micromatch@4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@^1.28.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^7.4.2: + version "7.4.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1: + version "9.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +mitt@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mozjpeg@^7.0.0: + version "7.1.1" + resolved "https://registry.npmjs.org/mozjpeg/-/mozjpeg-7.1.1.tgz" + integrity sha512-iIDxWvzhWvLC9mcRJ1uSkiKaj4drF58oCqK2bITm5c2Jt6cJ8qQjSSru2PCaysG+hLIinryj8mgz5ZJzOYTv1A== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +muggle-string@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz" + integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +needle@^3.1.0: + version "3.3.1" + resolved "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz" + integrity sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q== + dependencies: + iconv-lite "^0.6.3" + sax "^1.2.4" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz" + integrity sha512-dxvWdI8gw6eAvk9BlPffgEoGfM7AdijoCwOEJge3e3ulT2XLgmU7KvvxprOaCu05Q1uGRHmOhHe1r6emZoKyFw== + +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +npm-conf@^1.1.0: + version "1.1.3" + resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz" + integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== + dependencies: + config-chain "^1.1.11" + pify "^3.0.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" + integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== + +nth-check@^2.0.1, nth-check@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" + integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== + +number-precision@^1.5.0: + version "1.6.0" + resolved "https://registry.npmjs.org/number-precision/-/number-precision-1.6.0.tgz" + integrity sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ== + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.2, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5: + version "1.1.8" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.7: + version "2.0.8" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +object.values@^1.1.7: + version "1.2.0" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^8.4.0: + version "8.4.2" + resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +optipng-bin@^7.0.0: + version "7.0.1" + resolved "https://registry.npmjs.org/optipng-bin/-/optipng-bin-7.0.1.tgz" + integrity sha512-W99mpdW7Nt2PpFiaO+74pkht7KEqkXkeRomdWXfEz3SALZ6hns81y/pm1dsGZ6ItUIfchiNIP6ORDr1zETU1jA== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + +os-filter-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz" + integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== + dependencies: + arch "^2.1.0" + +ow@^0.17.0: + version "0.17.0" + resolved "https://registry.npmjs.org/ow/-/ow-0.17.0.tgz" + integrity sha512-i3keDzDQP5lWIe4oODyDFey1qVrq2hXKTuTH2VpqwpYtzPiKZt2ziRI4NBQmgW40AnV5Euz17OyWweCb+bNEQA== + dependencies: + type-fest "^0.11.0" + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^0.4.0: + version "0.4.1" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz" + integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== + +p-event@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz" + integrity sha512-hV1zbA7gwqPVFcapfeATaNjQ3J0NuzorHPyG8GPL9g/Y/TplWVBVoCKCXL6Ej2zscrCEv195QNWJXuBH6XZuzA== + dependencies: + p-timeout "^1.1.1" + +p-event@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz" + integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== + dependencies: + p-timeout "^2.0.1" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz" + integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + 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== + dependencies: + p-limit "^3.0.2" + +p-map-series@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz" + integrity sha512-4k9LlvY6Bo/1FcIdV33wqZQES0Py+iKISU9Uc8p8AjWoZPnFKMpVIVD3s0EYn4jzLh1I+WeUZkJ0Yoa4Qfw3Kg== + dependencies: + p-reduce "^1.0.0" + +p-pipe@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz" + integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz" + integrity sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ== + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz" + integrity sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA== + dependencies: + p-finally "^1.0.0" + +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + 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== + dependencies: + callsites "^3.0.0" + +parse-entities@^1.0.2, parse-entities@^1.1.0: + version "1.2.2" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz" + integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" + integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" + integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz" + integrity sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pify@^4.0.0, pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinia@^2.0.23: + version "2.1.7" + resolved "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz" + integrity sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ== + dependencies: + "@vue/devtools-api" "^6.5.0" + vue-demi ">=0.14.5" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pngquant-bin@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/pngquant-bin/-/pngquant-bin-6.0.1.tgz" + integrity sha512-Q3PUyolfktf+hYio6wsg3SanQzEU/v8aICg/WpzxXcuCMRb7H2Q81okfpcEztbMvw25ILjd3a87doj2N9kvbpQ== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.1" + execa "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-html@^0.36.0: + version "0.36.0" + resolved "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz" + integrity sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw== + dependencies: + htmlparser2 "^3.10.0" + +postcss-html@^1.0.0, postcss-html@^1.5.0: + version "1.6.0" + resolved "https://registry.npmjs.org/postcss-html/-/postcss-html-1.6.0.tgz" + integrity sha512-OWgQ9/Pe23MnNJC0PL4uZp8k0EDaUvqpJFSiwFxOLClAhmD7UEisyhO3x5hVsD4xFrjReVTXydlrMes45dJ71w== + dependencies: + htmlparser2 "^8.0.0" + js-tokens "^8.0.0" + postcss "^8.4.0" + postcss-safe-parser "^6.0.0" + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-jsx@^0.36.0: + version "0.36.4" + resolved "https://registry.npmjs.org/postcss-jsx/-/postcss-jsx-0.36.4.tgz" + integrity sha512-jwO/7qWUvYuWYnpOb0+4bIIgJt7003pgU3P6nETBLaOyBXuTD55ho21xnals5nBrlpTIFodyd3/jBi6UO3dHvA== + dependencies: + "@babel/core" ">=7.2.2" + +postcss-less@^3.1.0: + version "3.1.4" + resolved "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz" + integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA== + dependencies: + postcss "^7.0.14" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-markdown@^0.36.0: + version "0.36.0" + resolved "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.36.0.tgz" + integrity sha512-rl7fs1r/LNSB2bWRhyZ+lM/0bwKv9fhl38/06gF6mKMo/NPnp55+K1dSTosSVjFZc0e1ppBlu+WT91ba0PMBfQ== + dependencies: + remark "^10.0.1" + unist-util-find-all-after "^1.0.2" + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz" + integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig== + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-reporter@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz" + integrity sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw== + dependencies: + chalk "^2.4.1" + lodash "^4.17.11" + log-symbols "^2.2.0" + postcss "^7.0.7" + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz" + integrity sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw== + +postcss-safe-parser@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz" + integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== + dependencies: + postcss "^7.0.26" + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss-sass@^0.3.5: + version "0.3.5" + resolved "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.3.5.tgz" + integrity sha512-B5z2Kob4xBxFjcufFnhQ2HqJQ2y/Zs/ic5EZbCywCkxKd756Q40cIQ/veRDwSrw1BF6+4wUgmpm0sBASqVi65A== + dependencies: + gonzales-pe "^4.2.3" + postcss "^7.0.1" + +postcss-scss@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz" + integrity sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA== + dependencies: + postcss "^7.0.6" + +postcss-selector-parser@^3.1.0: + version "3.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.15: + version "6.0.16" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz" + integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sorting@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-4.1.0.tgz" + integrity sha512-r4T2oQd1giURJdHQ/RMb72dKZCuLOdWx2B/XhXN1Y1ZdnwXsKH896Qz6vD4tFy9xSjpKNYhlZoJmWyhH/7JUQw== + dependencies: + lodash "^4.17.4" + postcss "^7.0.0" + +postcss-sorting@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz" + integrity sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g== + +postcss-syntax@^0.36.2, postcss-syntax@>=0.36.0: + version "0.36.2" + resolved "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz" + integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== + +postcss-value-parser@^3.3.0: + version "3.3.1" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss-value-parser@^4.1.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^7.0.0, postcss@^7.0.13, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.32, postcss@>=5.0.0: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^7.0.1: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^7.0.14: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^7.0.6: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^7.0.7: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.3.11, postcss@^8.3.3, postcss@^8.3.9, postcss@^8.4.0, postcss@^8.4.18, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.38, postcss@>=8.0.9: + version "8.4.38" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" + integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg== + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" + integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.8.8, prettier@>=2.0.0: + version "2.8.8" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +query-string@^8.0.3: + version "8.2.0" + resolved "https://registry.npmjs.org/query-string/-/query-string-8.2.0.tgz" + integrity sha512-tUZIw8J0CawM5wyGBiDOAp7ObdRQh4uBor/fUR9ZjmbZVvw95OD9If4w3MQxr99rg0DJZ/9CIORcpEqU5hQG7g== + dependencies: + decode-uri-component "^0.4.1" + filter-obj "^5.1.0" + split-on-first "^3.0.0" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz" + integrity sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" + integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz" + integrity sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw== + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" + integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^2.0.0, readable-stream@^2.3.0, readable-stream@^2.3.5: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^3.1.1: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@3: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" + integrity sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g== + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz" + integrity sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw== + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +remark-parse@^6.0.0: + version "6.0.3" + resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-6.0.3.tgz" + integrity sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-stringify@^6.0.0: + version "6.0.4" + resolved "https://registry.npmjs.org/remark-stringify/-/remark-stringify-6.0.4.tgz" + integrity sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg== + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^1.1.0" + mdast-util-compact "^1.0.0" + parse-entities "^1.0.2" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^1.0.1" + unherit "^1.0.4" + xtend "^4.0.1" + +remark@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz" + integrity sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ== + dependencies: + remark-parse "^6.0.0" + remark-stringify "^6.0.0" + unified "^7.0.0" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.4, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + integrity sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A== + dependencies: + is-finite "^1.0.0" + +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz" + integrity sha512-vuNYXC7gG7IeVNBC1xUllqCcZKRbJoSPOBhnTEcAIiKCsbuef6zO3F0Rve3isPMMoNoQRWjQwbAgAjHUHniyEA== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resize-detector@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz" + integrity sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ== + +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0, resolve-from@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-global@^1.0.0, resolve-global@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve@^1.1.7, resolve@^1.10.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" + integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.0: + version "1.3.1" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz" + integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + +rimraf@^2.5.4: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rollup-plugin-visualizer@^5.8.2: + version "5.12.0" + resolved "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz" + integrity sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ== + dependencies: + open "^8.4.0" + picomatch "^2.3.1" + source-map "^0.7.4" + yargs "^17.5.1" + +rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, rollup@^3.9.1, "rollup@2.x || 3.x || 4.x": + version "3.29.4" + resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz" + integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + optionalDependencies: + fsevents "~2.3.2" + +rollup@^2.77.2: + version "2.79.1" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + +rollup@^2.79.1: + version "2.79.1" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +scroll-into-view-if-needed@^2.2.28: + version "2.2.31" + resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz" + integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA== + dependencies: + compute-scroll-into-view "^1.0.20" + +seek-bzip@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz" + integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== + dependencies: + commander "^2.8.1" + +semver-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz" + integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== + +semver-truncate@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz" + integrity sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w== + dependencies: + semver "^5.3.0" + +semver@^5.3.0: + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^5.6.0: + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.5.4, semver@^7.6.0: + version "7.6.0" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sort-keys-length@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz" + integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw== + dependencies: + sort-keys "^1.0.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" + integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg== + dependencies: + is-plain-obj "^1.0.0" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" + integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== + dependencies: + is-plain-obj "^1.0.0" + +sortablejs@^1.15.0: + version "1.15.2" + resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz" + integrity sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA== + +source-map-js@^1.0.2, source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.17" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz" + integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== + +specificity@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz" + integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== + +split-on-first@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz" + integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split2@^3.0.0, split2@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +squeak@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz" + integrity sha512-YQL1ulInM+ev8nXX7vfXsCsDh6IqXlrremc1hzi77776BtpWgYJUMto3UM05GSAaGzJgWekszjoKDrVNB5XG+A== + dependencies: + chalk "^1.0.0" + console-stream "^0.1.1" + lpad-align "^1.0.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" + integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string-argv@0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +stringify-entities@^1.0.1: + version "1.3.2" + resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz" + integrity sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A== + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-hexadecimal "^1.0.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" + integrity sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA== + dependencies: + get-stdin "^4.0.1" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz" + integrity sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.1: + 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== + +strip-outer@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" + integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== + dependencies: + escape-string-regexp "^1.0.2" + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz" + integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== + +stylelint-config-html@>=1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz" + integrity sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ== + +stylelint-config-prettier@^9.0.3: + version "9.0.5" + resolved "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz" + integrity sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA== + +stylelint-config-rational-order@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/stylelint-config-rational-order/-/stylelint-config-rational-order-0.1.2.tgz" + integrity sha512-Qo7ZQaihCwTqijfZg4sbdQQHtugOX/B1/fYh018EiDZHW+lkqH9uHOnsDwDPGZrYJuB6CoyI7MZh2ecw2dOkew== + dependencies: + stylelint "^9.10.1" + stylelint-order "^2.2.1" + +stylelint-config-recommended-vue@^1.4.0: + version "1.5.0" + resolved "https://registry.npmjs.org/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.5.0.tgz" + integrity sha512-65TAK/clUqkNtkZLcuytoxU0URQYlml+30Nhop7sRkCZ/mtWdXt7T+spPSB3KMKlb+82aEVJ4OrcstyDBdbosg== + dependencies: + semver "^7.3.5" + stylelint-config-html ">=1.0.0" + stylelint-config-recommended ">=6.0.0" + +stylelint-config-recommended@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-9.0.0.tgz" + integrity sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ== + +stylelint-config-recommended@>=6.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz" + integrity sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ== + +stylelint-config-standard@^29.0.0: + version "29.0.0" + resolved "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-29.0.0.tgz" + integrity sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg== + dependencies: + stylelint-config-recommended "^9.0.0" + +stylelint-order@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/stylelint-order/-/stylelint-order-2.2.1.tgz" + integrity sha512-019KBV9j8qp1MfBjJuotse6MgaZqGVtXMc91GU9MsS9Feb+jYUvUU3Z8XiClqPdqJZQ0ryXQJGg3U3PcEjXwfg== + dependencies: + lodash "^4.17.10" + postcss "^7.0.2" + postcss-sorting "^4.1.0" + +stylelint-order@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/stylelint-order/-/stylelint-order-5.0.0.tgz" + integrity sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw== + dependencies: + postcss "^8.3.11" + postcss-sorting "^7.0.1" + +stylelint@^14.0.0, stylelint@^14.10.0, stylelint@^14.14.0, stylelint@^14.16.1, stylelint@^16.0.0, "stylelint@>= 11.x < 15", stylelint@>=14.0.0: + version "14.16.1" + resolved "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz" + integrity sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A== + dependencies: + "@csstools/selector-specificity" "^2.0.2" + balanced-match "^2.0.0" + colord "^2.9.3" + cosmiconfig "^7.1.0" + css-functions-list "^3.1.0" + debug "^4.3.4" + fast-glob "^3.2.12" + fastest-levenshtein "^1.0.16" + file-entry-cache "^6.0.1" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.2.0" + ignore "^5.2.1" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.26.0" + mathml-tag-names "^2.1.3" + meow "^9.0.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.19" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + style-search "^0.1.0" + supports-hyperlinks "^2.3.0" + svg-tags "^1.0.0" + table "^6.8.1" + v8-compile-cache "^2.3.0" + write-file-atomic "^4.0.2" + +stylelint@^9.10.1, "stylelint@^9.10.1 || ^10.0.0": + version "9.10.1" + resolved "https://registry.npmjs.org/stylelint/-/stylelint-9.10.1.tgz" + integrity sha512-9UiHxZhOAHEgeQ7oLGwrwoDR8vclBKlSX7r4fH0iuu0SfPwFaLkb1c7Q2j1cqg9P7IDXeAV2TvQML/fRQzGBBQ== + dependencies: + autoprefixer "^9.0.0" + balanced-match "^1.0.0" + chalk "^2.4.1" + cosmiconfig "^5.0.0" + debug "^4.0.0" + execall "^1.0.0" + file-entry-cache "^4.0.0" + get-stdin "^6.0.0" + global-modules "^2.0.0" + globby "^9.0.0" + globjoin "^0.1.4" + html-tags "^2.0.0" + ignore "^5.0.4" + import-lazy "^3.1.0" + imurmurhash "^0.1.4" + known-css-properties "^0.11.0" + leven "^2.1.0" + lodash "^4.17.4" + log-symbols "^2.0.0" + mathml-tag-names "^2.0.1" + meow "^5.0.0" + micromatch "^3.1.10" + normalize-selector "^0.2.0" + pify "^4.0.0" + postcss "^7.0.13" + postcss-html "^0.36.0" + postcss-jsx "^0.36.0" + postcss-less "^3.1.0" + postcss-markdown "^0.36.0" + postcss-media-query-parser "^0.2.3" + postcss-reporter "^6.0.0" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^4.0.0" + postcss-sass "^0.3.5" + postcss-scss "^2.0.0" + postcss-selector-parser "^3.1.0" + postcss-syntax "^0.36.2" + postcss-value-parser "^3.3.0" + resolve-from "^4.0.0" + signal-exit "^3.0.2" + slash "^2.0.0" + specificity "^0.4.1" + string-width "^3.0.0" + style-search "^0.1.0" + sugarss "^2.0.0" + svg-tags "^1.0.0" + table "^5.0.0" + +sucrase@^3.32.0: + version "3.35.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + 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" + +sugarss@*, sugarss@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz" + integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ== + dependencies: + postcss "^7.0.2" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +svgo@^2.1.0, svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +table@^5.0.0: + version "5.4.6" + resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +table@^6.8.1: + version "6.8.2" + resolved "https://registry.npmjs.org/table/-/table-6.8.2.tgz" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tailwindcss@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz" + integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.0" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" + integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== + +tempfile@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz" + integrity sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA== + dependencies: + temp-dir "^1.0.0" + uuid "^3.0.1" + +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + 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== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +through@^2.3.8, "through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" + integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + 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== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" + integrity sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw== + +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz" + integrity sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA== + +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + +trim-repeated@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" + integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== + dependencies: + escape-string-regexp "^1.0.2" + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +ts-interface-checker@^0.1.9: + 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== + +ts-node@^10.8.1, ts-node@>=10: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.3.0: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typescript@*, typescript@^4.8.4, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.4.4: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +"typescript@^4.6.4 || ^5.2.2", typescript@>=2.7, typescript@>=4: + version "5.4.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +typescript@>=4.9.5: + version "5.4.5" + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unbzip2-stream@^1.0.9: + version "1.4.3" + resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unified@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz" + integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== + dependencies: + "@types/unist" "^2.0.0" + "@types/vfile" "^3.0.0" + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^3.0.0" + x-is-string "^0.1.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" + integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== + +unist-util-find-all-after@^1.0.2: + version "1.0.5" + resolved "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz" + integrity sha512-lWgIc3rrTMTlK1Y0hEuL+k+ApzFk78h+lsaa2gHf63Gp5Ww+mt11huDniuaoq1H+XMK2lIIjjPkncxXcDp3QDw== + dependencies: + unist-util-is "^3.0.0" + +unist-util-is@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz" + integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== + +unist-util-remove-position@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz" + integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A== + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz" + integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== + +unist-util-stringify-position@^4.0.0: + 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== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^2.0.0: + version "2.1.2" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz" + integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== + dependencies: + unist-util-is "^3.0.0" + +unist-util-visit@^1.1.0: + version "1.4.1" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz" + integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== + dependencies: + unist-util-visit-parents "^2.0.0" + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unplugin-vue-components@^0.24.1: + version "0.24.1" + resolved "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.24.1.tgz" + integrity sha512-T3A8HkZoIE1Cja95xNqolwza0yD5IVlgZZ1PVAGvVCx8xthmjsv38xWRCtHtwl+rvZyL9uif42SRkDGw9aCfMA== + dependencies: + "@antfu/utils" "^0.7.2" + "@rollup/pluginutils" "^5.0.2" + chokidar "^3.5.3" + debug "^4.3.4" + fast-glob "^3.2.12" + local-pkg "^0.4.3" + magic-string "^0.30.0" + minimatch "^7.4.2" + resolve "^1.22.1" + unplugin "^1.1.0" + +unplugin@^1.1.0: + version "1.10.1" + resolved "https://registry.npmjs.org/unplugin/-/unplugin-1.10.1.tgz" + integrity sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg== + dependencies: + acorn "^8.11.3" + chokidar "^3.6.0" + webpack-sources "^3.2.3" + webpack-virtual-modules "^0.6.1" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" + integrity sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA== + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" + integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== + dependencies: + prepend-http "^2.0.0" + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz" + integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^3.0.1: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-compile-cache@^2.3.0: + version "2.4.0" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz" + integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vfile-location@^2.0.0: + version "2.0.6" + resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz" + integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== + +vfile-message@*: + version "4.0.2" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile-message@^1.0.0: + version "1.1.1" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz" + integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz" + integrity sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ== + dependencies: + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +vite-plugin-compression@^0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz" + integrity sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg== + dependencies: + chalk "^4.1.2" + debug "^4.3.3" + fs-extra "^10.0.0" + +vite-plugin-eslint@^1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/vite-plugin-eslint/-/vite-plugin-eslint-1.8.1.tgz" + integrity sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang== + dependencies: + "@rollup/pluginutils" "^4.2.1" + "@types/eslint" "^8.4.5" + rollup "^2.77.2" + +vite-plugin-imagemin@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/vite-plugin-imagemin/-/vite-plugin-imagemin-0.6.1.tgz" + integrity sha512-cP7LDn8euPrji7WYtDoNQpJEB9nkMxJHm/A+QZnvMrrCSuyo/clpMy/T1v7suDXPBavsDiDdFdVQB5p7VGD2cg== + dependencies: + "@types/imagemin" "^7.0.1" + "@types/imagemin-gifsicle" "^7.0.1" + "@types/imagemin-jpegtran" "^5.0.1" + "@types/imagemin-mozjpeg" "^8.0.1" + "@types/imagemin-optipng" "^5.2.1" + "@types/imagemin-svgo" "^10.0.0" + "@types/imagemin-webp" "^7.0.0" + "@types/svgo" "^2.6.1" + chalk "^4.1.2" + debug "^4.3.3" + esbuild "^0.14.14" + fs-extra "^10.0.0" + gifsicle "5.2.0" + imagemin "^7.0.1" + imagemin-gifsicle "^7.0.0" + imagemin-jpegtran "^7.0.0" + imagemin-mozjpeg "^9.0.0" + imagemin-optipng "^8.0.0" + imagemin-pngquant "^9.0.2" + imagemin-svgo "^9.0.0" + imagemin-webp "^6.0.0" + jpegtran-bin "^6.0.1" + pathe "^0.2.0" + +vite-svg-loader@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-3.6.0.tgz" + integrity sha512-bZJffcgCREW57kNkgMhuNqeDznWXyQwJ3wKrRhHLMMzwDnP5jr3vXW3cqsmquRR7VTP5mLdKj1/zzPPooGUuPw== + dependencies: + "@vue/compiler-sfc" "^3.2.20" + svgo "^2.7.0" + +vite@^3.0.0, vite@^3.2.5, vite@>=2, vite@>=2.0.0: + version "3.2.10" + resolved "https://registry.npmjs.org/vite/-/vite-3.2.10.tgz" + integrity sha512-Dx3olBo/ODNiMVk/cA5Yft9Ws+snLOXrhLtrI3F4XLt4syz2Yg8fayZMWScPKoz12v5BUv7VEmQHnsfpY80fYw== + dependencies: + esbuild "^0.15.9" + postcss "^8.4.18" + resolve "^1.22.1" + rollup "^2.79.1" + optionalDependencies: + fsevents "~2.3.2" + +vue-demi@*, vue-demi@>=0.14.5: + version "0.14.7" + resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz" + integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA== + +vue-demi@^0.13.11: + version "0.13.11" + resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz" + integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== + +vue-echarts@^6.2.3: + version "6.7.0" + resolved "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.7.0.tgz" + integrity sha512-dP2HKr/I1MswH5OzkZqR/JmSpFpgJmJrOH7FjWsch6SkcicmUHV0qs9h5CWKWNbs201I6CX+l8zw0F0RUKLAtA== + dependencies: + resize-detector "^0.3.0" + vue-demi "^0.13.11" + +vue-eslint-parser@^9.4.2: + version "9.4.2" + resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz" + integrity sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue-i18n@^9.2.2: + version "9.13.1" + resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz" + integrity sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg== + dependencies: + "@intlify/core-base" "9.13.1" + "@intlify/shared" "9.13.1" + "@vue/devtools-api" "^6.5.0" + +vue-router@^4.0.14: + version "4.3.2" + resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz" + integrity sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q== + dependencies: + "@vue/devtools-api" "^6.5.1" + +vue-template-compiler@^2.7.14: + version "2.7.16" + resolved "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz" + integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ== + dependencies: + de-indent "^1.0.2" + he "^1.2.0" + +vue-tsc@^1.0.14: + version "1.8.27" + resolved "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz" + integrity sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg== + dependencies: + "@volar/typescript" "~1.11.1" + "@vue/language-core" "1.8.27" + semver "^7.5.4" + +"vue@^2.6.12 || ^3.1.1", "vue@^2.6.14 || ^3.3.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.1.0, vue@^3.2.0, vue@^3.2.25, vue@^3.2.40, "vue@2 || 3", vue@3.4.24: + version "3.4.24" + resolved "https://registry.npmjs.org/vue/-/vue-3.4.24.tgz" + integrity sha512-NPdx7dLGyHmKHGRRU5bMRYVE+rechR+KDU5R2tSTNG36PuMwbfAJ+amEvOAw7BPfZp5sQulNELSLm5YUkau+Sg== + dependencies: + "@vue/compiler-dom" "3.4.24" + "@vue/compiler-sfc" "3.4.24" + "@vue/runtime-dom" "3.4.24" + "@vue/server-renderer" "3.4.24" + "@vue/shared" "3.4.24" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack-virtual-modules@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz" + integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz" + integrity sha512-GojqklwG8gpzOVEVki5KudKNoq7MbbjYZCbyWzEz7tyPA7eleiE0+ePwOWQQRb5fm86rD3S8Tc0tSFf3AOv50w== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yaml@^2.3.4: + version "2.4.1" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + +yaml@2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + +yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + +yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.0.0, yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zrender@5.5.0: + version "5.5.0" + resolved "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz" + integrity sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w== + dependencies: + tslib "2.3.0" diff --git a/resources/database.sql b/resources/database.sql new file mode 100644 index 0000000..19ddf00 --- /dev/null +++ b/resources/database.sql @@ -0,0 +1,2 @@ +CREATE DATABASE passiflora_system; +CREATE DATABASE passiflora_storage; \ No newline at end of file