diff --git a/.air.toml b/.air.toml index 2186020..ccc623f 100644 --- a/.air.toml +++ b/.air.toml @@ -3,7 +3,7 @@ testdata_dir = "testdata" tmp_dir = "tmp" [build] - args_bin = ["run"] + args_bin = [] bin = "./tmp/main" cmd = "go build -o ./tmp/main ./cmd/lite/lite.go" delay = 0 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a0ae8ea --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +web/node_modules diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..6059d66 --- /dev/null +++ b/.env.template @@ -0,0 +1,19 @@ +ADMIN_PASSWORD=password +DSN=postgres://postgres:root@localhost:5432/fivemanage-lite-dev?sslmode=disable + +API_TOKEN_HMAC_SECRET=<32 bytes secret> + +S3_PROVIDER=minio # minio, r2, s3 +AWS_ACCESS_KEY_ID=minioadmin +AWS_SECRET_ACCESS_KEY=minioadmin123 +AWS_ENDPOINT=http://localhost:9000 +AWS_BUCKET=lite-dev +AWS_REGION=eu-west-1 + +CLICKHOUSE_HOST=localhost:19000 +CLICKHOUSE_DATABASE=default +CLICKHOUSE_USERNAME=default +CLICKHOUSE_PASSWORD=password + +VERSION=dev # dev only +ENV=dev # dev only diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..005909a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/web" + schedule: + interval: "weekly" + - package-ecosystem: "docker" + directory: "/build/package" + schedule: + interval: "weekly" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..326bd8a --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,20 @@ +name-template: 'Nightly Release' +categories: + - title: 'Features' + labels: + - 'feature' + - 'feat' + - 'enhancement' + - title: 'Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: 'Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..444bd23 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,57 @@ +name: Lint + +on: + pull_request: + types: + - opened + - synchronize + +jobs: + lint_frontend: + name: Lint frontend + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./web + + strategy: + matrix: + node-version: [22] + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache-dependency-path: ./web/pnpm-lock.yaml + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install + + - name: Run lint + run: pnpm lint + + lint_backend: + name: Lint backend + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: stable + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.1 diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml new file mode 100644 index 0000000..5c5d42b --- /dev/null +++ b/.github/workflows/prerelease.yml @@ -0,0 +1,85 @@ +name: Pre Release + +on: + pull_request: + types: + - closed + branches: + - develop + +permissions: + contents: read + +env: + dockerImage: 'fivemanage/lite' + +jobs: + release_nightly: + if: github.event.pull_request.merged == true + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: '0' + + # Docker stuff + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + + - name: Bump version and push tag + uses: anothrNick/github-tag-action@v1 + id: tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_BRANCHES: master + MAJOR_STRING_TOKEN: "BREAKING*CHANGE|*#major*" + MINOR_STRING_TOKEN: "*feat*|*#minor*" + PATCH_STRING_TOKEN: "*fix*|*chore*|*docs*|*update*|#patch|*tweak*" + DEFAULT_BRANCH: develop + PRERELEASE: true + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.dockerImage }} + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + + # more docker stuff + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + file: ./build/package/Dockerfile + platforms: linux/amd64,linux/arm64 + sbom: true + push: true + provenance: mode=max + annotations: ${{ steps.meta.outputs.annotations }} + tags: | + ${{ env.dockerImage }}:${{ steps.tag.outputs.new_tag }} + + - uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + name: 'Nightly Release ${{ steps.tag.outputs.new_tag }}' + tag: ${{ steps.tag.outputs.new_tag }} + version: ${{ steps.tag.outputs.new_tag }} + prerelease: true + publish: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index fba0d18..6429339 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # Logs -logs *.log npm-debug.log* yarn-debug.log* diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0969e20 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,105 @@ +# Functional Source License, Version 1.1, ALv2 Future License + +## Abbreviation + +FSL-1.1-ALv2 + +## Notice + +Copyright 2025 Fivemanage AS + +## Terms and Conditions + +### Licensor ("We") + +The party offering the Software under these Terms and Conditions. + +### The Software + +The "Software" is each version of the software that we make available under +these Terms and Conditions, as indicated by our inclusion of these Terms and +Conditions with the Software. + +### License Grant + +Subject to your compliance with this License Grant and the Patents, +Redistribution and Trademark clauses below, we hereby grant you the right to +use, copy, modify, create derivative works, publicly perform, publicly display +and redistribute the Software for any Permitted Purpose identified below. + +### Permitted Purpose + +A Permitted Purpose is any purpose other than a Competing Use. A Competing Use +means making the Software available to others in a commercial product or +service that: + +1. substitutes for the Software; + +2. substitutes for any other product or service we offer using the Software + that exists as of the date we make the Software available; or + +3. offers the same or substantially similar functionality as the Software. + +Permitted Purposes specifically include using the Software: + +1. for your internal use and access; + +2. for non-commercial education; + +3. for non-commercial research; and + +4. in connection with professional services that you provide to a licensee + using the Software in accordance with these Terms and Conditions. + +### Patents + +To the extent your use for a Permitted Purpose would necessarily infringe our +patents, the license grant above includes a license under our patents. If you +make a claim against any party that the Software infringes or contributes to +the infringement of any patent, then your patent license to the Software ends +immediately. + +### Redistribution + +The Terms and Conditions apply to all copies, modifications and derivatives of +the Software. + +If you redistribute any copies, modifications or derivatives of the Software, +you must include a copy of or a link to these Terms and Conditions and not +remove any copyright notices provided in or with the Software. + +### Disclaimer + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. + +IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE +SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, +EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. + +### Trademarks + +Except for displaying the License Details and identifying us as the origin of +the Software, you have no right under these Terms and Conditions to use our +trademarks, trade names, service marks or product names. + +## Grant of Future License + +We hereby irrevocably grant you an additional license to use the Software under +the Apache License, Version 2.0 that is effective on the second anniversary of +the date we make the Software available. On or after that date, you may use the +Software under the Apache License, Version 2.0, in which case the following +will apply: + +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 + +http://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. diff --git a/README.md b/README.md index e69de29..a1a8b22 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,85 @@ +# Fivemanage Lite + +## Deployment +To run Fivemanage Lite, you need to set up a MySQL or PostgreSQL database and an object storage service (S3 compatible). + +### Database +You can use either MySQL or PostgreSQL. The default is MySQL. + +### Object Storage +Currently only S3 or compatible object storage is supported. +- s3 +- r2 +- minio + +Azure and GCP are not supported yet. + +### Docker +You can have a look at the `docker-compose.yml` file in the `deployments` folder. + + +### Environment variables +You can copy the `.env.template` file to `.env` and set the values. + +```env +ADMIN_PASSWORD=verysecurepassword +DSN=postgres://username:password@host:5432/fivemanage-lite?sslmode=disable + +API_TOKEN_HMAC_SECRET=<32 bytes secret> + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_ENDPOINT= +AWS_BUCKET= +AWS_REGION= + +CLICKHOUSE_HOST=localhost:19000 +CLICKHOUSE_DATABASE=default +# for prod, you should create a new user with a password, or change the default user +CLICKHOUSE_USERNAME=default +CLICKHOUSE_PASSWORD=password +``` + +## Development + +### Prerequisites + +- Go: 1.23 or later +- Node.js: 20.x or later +- pnpm: 9.11.x or later +- air: hot reload for Go (https://github.com/air-verse/air) + +### Setup + +1. Install node_modules in `/web`. +2. Either run `go mod download` in the root directory, or just let `air` handle it for you, but simply running `air` in the root directory. +3. Set up docker. + 1. Run `docker compose -f deployments/docker-compose.yml up -d` +4. Set up environment variables + +```env +ADMIN_PASSWORD=password +DSN=postgres://postgres:root@localhost:5432/fivemanage-lite-dev?sslmode=disable + +API_TOKEN_HMAC_SECRET=<32 bytes secret> + +AWS_ACCESS_KEY_ID=xxxx +AWS_SECRET_ACCESS_KEY=xxxx +AWS_ENDPOINT= +AWS_BUCKET= +AWS_REGION= + +CLICKHOUSE_HOST=localhost:19000 +CLICKHOUSE_DATABASE=default +CLICKHOUSE_USERNAME=default +CLICKHOUSE_PASSWORD=password +``` + +### Running the application + +Migrations for Clickhouse and PostgreSQL are run automatically when the application starts. + + +Start the actual app: +1. Run `air` or `go run cmd/lite/lite.go` in the root directory to start the Go application +2. In `web/`, run `pnpm dev` to start the React application. diff --git a/api/admin.go b/api/admin.go new file mode 100644 index 0000000..0d9b347 --- /dev/null +++ b/api/admin.go @@ -0,0 +1,12 @@ +package api + +type CreateMemberRequest struct { + Username string `json:"username" validate:"required"` + Email string `json:"email"` +} + +// we should probably, probably probably use protobufs this point +type CreateMemberResponse struct { + Username string `json:"username"` + Password string `json:"password"` +} diff --git a/api/dataset.go b/api/dataset.go new file mode 100644 index 0000000..431183f --- /dev/null +++ b/api/dataset.go @@ -0,0 +1,53 @@ +package api + +import "time" + +type Dataset struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description"` + RetentionDays int `json:"retentionDays"` + OrganizationID string `json:"organizationId"` +} + +type DatasetField struct { + Field string `json:"field"` + // Type is either "String" or "Numeric" + Type string `json:"type"` +} + +type DatasetLog struct { + Timestamp time.Time `json:"Timestamp"` + DatasetId string `json:"DatasetId"` + TraceId string `json:"TraceId"` + TeamId string `json:"TeamId"` + Body string `json:"Body"` + Attributes map[string]string `json:"Metadata"` +} + +type DatasetLogsResponse struct { + Data []DatasetLog `json:"data"` + Meta struct { + TotalRowCount int `json:"totalRowCount"` + } `json:"meta"` +} + +type ListLogsSchema struct { + OrganizationID string `json:"organizationId"` + DatasetID string `json:"datasetId"` + Levels string `json:"levels"` + FromDate string `json:"fromDate"` + ToDate string `json:"toDate"` + Metadata string `json:"metadata"` + // we convert this to a filter object for the querybuilder + Filter string `json:"filter"` + Cursor int `json:"cursor"` +} + +type DatasetFilter struct { + Field string `json:"field"` + Operator string `json:"operator"` + Value *string `json:"value,omitzero"` + Type *string `json:"type,omitzero"` + Children []DatasetFilter `json:"children,omitzero"` +} diff --git a/api/file.go b/api/file.go index d19d6cd..8db306e 100644 --- a/api/file.go +++ b/api/file.go @@ -1,8 +1,24 @@ package api -import "mime/multipart" +import ( + "mime/multipart" + "time" +) type UploadFile struct { FileHeader *multipart.FileHeader File multipart.File } + +type Asset struct { + ID string `json:"id"` + Key string `json:"key"` + Size int64 `json:"size"` + Type string `json:"type"` + CreatedAt time.Time `json:"createdAt"` +} + +type AssetResponse struct { + StorageFiles []*Asset `json:"files"` + TotalCount int `json:"totalCount"` +} diff --git a/api/log.go b/api/log.go new file mode 100644 index 0000000..19a4406 --- /dev/null +++ b/api/log.go @@ -0,0 +1,8 @@ +package api + +type Log struct { + Level string `json:"level"` + Message string `json:"message"` + Resource string `json:"resource"` + Metadata map[string]any `json:"metadata"` +} diff --git a/api/organization.go b/api/organization.go new file mode 100644 index 0000000..6351297 --- /dev/null +++ b/api/organization.go @@ -0,0 +1,10 @@ +package api + +type Organization struct { + ID string `json:"id"` + Name string `json:"name"` +} + +type CreateOrganizationRequest struct { + Name string `json:"name"` +} diff --git a/api/token.go b/api/token.go index b365994..e0be037 100644 --- a/api/token.go +++ b/api/token.go @@ -1,8 +1,9 @@ package api type CreateTokenRequest struct { - Identifier string `json:"identifier"` - Type string `json:"type"` + Identifier string `json:"identifier"` + Type string `json:"type"` + OrganizationID string `json:"omitzero"` } type CreateTokenResponse struct { diff --git a/build/package/Dockerfile b/build/package/Dockerfile index a3bbfab..3262ac9 100644 --- a/build/package/Dockerfile +++ b/build/package/Dockerfile @@ -1,5 +1,24 @@ -# Builder stage -FROM --platform=arm64 golang:1.22-alpine3.19 as builder +# Frontend +FROM node:22-slim AS frontend + +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +WORKDIR /frontend + +COPY web/pnpm-lock.yaml . + +RUN pnpm fetch + +COPY web . + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build + + +# Backend +FROM golang:1.24 AS backend WORKDIR /app @@ -9,13 +28,20 @@ RUN go mod download COPY . . +COPY --from=frontend /frontend/dist internal/http/dist + +ENV CGO_ENABLED=0 RUN go build -o /app/main ./cmd/lite/lite.go -# Final stage -FROM --platform=arm64 alpine:3.14 +# Application +FROM gcr.io/distroless/static-debian12 WORKDIR /app -COPY --from=builder /app/main /app/main +COPY --from=backend /app/main /app/main + +EXPOSE 8080 + +USER nonroot:nonroot CMD ["/app/main"] diff --git a/cmd/lite/lite.go b/cmd/lite/lite.go index 5a7604b..eeb0a56 100644 --- a/cmd/lite/lite.go +++ b/cmd/lite/lite.go @@ -3,115 +3,211 @@ package main import ( "context" "fmt" - "log" + "log/slog" nethttp "net/http" "os" "os/signal" "syscall" "time" + "github.com/fivemanage/lite/internal/clickhouse" "github.com/fivemanage/lite/internal/database" "github.com/fivemanage/lite/internal/http" "github.com/fivemanage/lite/internal/service/auth" + "github.com/fivemanage/lite/internal/service/dataset" "github.com/fivemanage/lite/internal/service/file" + "github.com/fivemanage/lite/internal/service/log" + "github.com/fivemanage/lite/internal/service/organization" "github.com/fivemanage/lite/internal/service/token" - "github.com/fivemanage/lite/internal/storage" "github.com/fivemanage/lite/migrate" + "github.com/fivemanage/lite/pkg/cache" + "github.com/fivemanage/lite/pkg/kafkaqueue" + "github.com/fivemanage/lite/pkg/logger" + "github.com/fivemanage/lite/pkg/otel" + "github.com/fivemanage/lite/pkg/storage" "github.com/joho/godotenv" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/uptrace/opentelemetry-go-extra/otelzap" ) -var ( - rootCmd = &cobra.Command{ - Use: "fivemanage", - Short: "Open-source, easy-to-use gaming-community management service.", - } - - // todo: otel & promhttp +var rootCmd = &cobra.Command{ + Use: "fivemanage", + Short: "Open-source, easy-to-use gaming-community management service.", + Run: func(cmd *cobra.Command, args []string) { + var err error - // this whole code can probably go into a 'run.go' file. - // just to not fill this file with shit - // its gonna be ugly anyways tho - runCmd = &cobra.Command{ - Use: "run", - Short: "Run fivemanage application", - Run: func(cmd *cobra.Command, args []string) { - var err error + port := viper.GetInt("port") + driver := viper.GetString("driver") + dsn := viper.GetString("dsn") - port := viper.GetInt("port") - driver := viper.GetString("driver") + logger.New() - err = godotenv.Load() - // TODO: Only for development - if err != nil { - log.Fatal("Error loading .env file. Probably becasue we're in production") - } + slog.Info("starting Fivemanage application") - db := database.New(driver, "") - store := db.Connect() - migrate.AutoMigrate(cmd.Context(), store) + otelShutdown, err := otel.SetupTracer() + if err != nil { + slog.Error("failed to setup OpenTelemetry", slog.Any("error", err)) + } - storageLayer := storage.New("s3") + defer func() { + slog.Info("attempting to shutdown OpenTelemetry...") + otelCtx, otelCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer otelCancel() - authservice := auth.New(store) - tokenservice := token.NewService(store) - fileservice := file.NewService(store, storageLayer) + if shutdownErr := otelShutdown(otelCtx); shutdownErr != nil { + slog.Error("failed to shutdown OpenTelemetry", slog.Any("error", shutdownErr)) + } else { + slog.Info("OpenTelemetry shutdown successfully.") + } + }() + + // this is kinda confusion, 'db' and 'store' + // maybe we shold call it like...connection and instance? + // store makes no fucking sense atleast + db := database.New(driver) + store := db.Connect(dsn) + + // run migrations for postgres + // and yes, it's ok to init this here + migrate.InitMigration(cmd.Context(), store) + migrate.AutoMigrate(cmd.Context(), store) + + // setup and run migrations for clickhouse + chConfig := &clickhouse.Config{ + Host: viper.GetString("clickhouse-host"), + Username: viper.GetString("clickhouse-username"), + Password: viper.GetString("clickhouse-password"), + Database: viper.GetString("clickhouse-database"), + } + + clickhouse.AutoMigrate(cmd.Context(), chConfig) + clickhouseClient := clickhouse.NewClient(chConfig) + + kafkaC := kafkaqueue.NewConsumer(otelzap.L()) + kafkaP := kafkaqueue.NewProducer(otelzap.L()) + + storageLayer := storage.New("s3") + // im not sure if we need to exit here. + // not all uers might want to use this for file uploads + if err := storageLayer.CreateBucket(cmd.Context()); err != nil { + slog.Warn("failed to create default bucket", slog.Any("error", err)) + } + + authService := auth.NewService(store) + tokenService := token.NewService(store) + fileService := file.NewService(store, storageLayer) + organizationService := organization.NewService(store) + datsetService := dataset.NewService(store, clickhouseClient) + logService := log.NewService(store, kafkaP, clickhouseClient, datsetService) + + worker := kafkaqueue.NewBatchWorker(clickhouseClient, kafkaC) + + // kinda not sure what I want to do with this + // at some point we might actually want to run more than one worker + go worker.ProcessMessages() + + memcache := cache.NewMemcache(0) + + server := http.NewServer( + authService, + tokenService, + fileService, + organizationService, + logService, + datsetService, + memcache, + ) + + // todo: check if we have an admin user + // if not, create an admin user with the ADMIN_PASSWORD ENV + err = authService.CreateAdminUser() + if err != nil { + slog.Error("failed to create admin user", slog.Any("error", err)) + return + } + + srv := &nethttp.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: server, + } + + go func() { + otelzap.S().Infof("Fivemanage is running on port %d", port) + if err := srv.ListenAndServe(); err != nil && err != nethttp.ErrServerClosed { + slog.Error("listen", slog.Any("error", err)) + os.Exit(1) + } + }() - server := http.NewServer( - authservice, - tokenservice, - fileservice, - ) + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + slog.Info("shutdown Server ...") - // todo: check if we have an admin user - // if not, create an admin user with the ADMIN_PASSWORD ENV - err = authservice.CreateAdminUser() - if err != nil { - logrus.WithError(err).Error("Failed to create admin user") - return - } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() - srv := &nethttp.Server{ - Addr: fmt.Sprintf("localhost:%d", port), - Handler: server, - } + if err := srv.Shutdown(ctx); err != nil { + slog.Error("server shutdown", slog.Any("error", err)) + os.Exit(1) + } - go func() { - fmt.Printf("Server is running on port %d...\n", port) - if err := srv.ListenAndServe(); err != nil && err != nethttp.ErrServerClosed { - log.Fatalf("listen: %s\n", err) - } - }() - - quit := make(chan os.Signal, 1) - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - log.Println("Shutdown Server ...") - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server Shutdown:", err) - } - select { - case <-ctx.Done(): - log.Println("timeout of 5 seconds.") - } - log.Println("Server exiting") - }, - } -) + <-ctx.Done() + slog.Info("server exiting") + }, +} func init() { - rootCmd.PersistentFlags().String("driver", "mysql", "Database driver") - runCmd.Flags().Int("port", 8080, "Port to serve Fivemanage") + slog.Info("initializing Fivemanage...") - viper.BindPFlag("driver", rootCmd.PersistentFlags().Lookup("driver")) - viper.BindPFlag("port", runCmd.Flags().Lookup("port")) + err := godotenv.Load() + if err != nil { + slog.Warn("Error loading .env file. Probably becasue we're in production", slog.Any("error", err)) + } - rootCmd.AddCommand(runCmd) + rootCmd.PersistentFlags().String("driver", "pg", "Database driver") + rootCmd.Flags().Int("port", 8080, "Port to serve Fivemanage") + rootCmd.Flags().String("dsn", "", "Database DSN") + // clickhouse + rootCmd.Flags().String("clickhouse-host", "localhost:9000", "Clickhouse host") + rootCmd.Flags().String("clickhouse-username", "default", "Clickhouse username") + rootCmd.Flags().String("clickhouse-password", "password", "Clickhouse password") + rootCmd.Flags().String("clickhouse-database", "default", "Clickhouse database") + // s3 + rootCmd.Flags().String("s3-provider", "minio", "S3 provider") + + // fuck me + if err := viper.BindPFlag("port", rootCmd.Flags().Lookup("port")); err != nil { + bindError(err) + } + if err := viper.BindPFlag("dsn", rootCmd.Flags().Lookup("dsn")); err != nil { + bindError(err) + } + if err := viper.BindEnv("driver", "DB_DRIVER"); err != nil { + bindError(err) + } + if err := viper.BindEnv("port", "PORT"); err != nil { + bindError(err) + } + if err := viper.BindEnv("dsn", "DSN"); err != nil { + bindError(err) + } + if err := viper.BindEnv("clickhouse-host", "CLICKHOUSE_HOST"); err != nil { + bindError(err) + } + if err := viper.BindEnv("clickhouse-username", "CLICKHOUSE_USERNAME"); err != nil { + bindError(err) + } + if err := viper.BindEnv("clickhouse-password", "CLICKHOUSE_PASSWORD"); err != nil { + bindError(err) + } + if err := viper.BindEnv("clickhouse-database", "CLICKHOUSE_DATABASE"); err != nil { + bindError(err) + } + if err := viper.BindEnv("s3-provider", "S3_PROVIDER"); err != nil { + bindError(err) + } rootCmd.AddCommand(migrate.RootCmd) migrate.RootCmd.AddCommand( @@ -128,3 +224,10 @@ func main() { panic(err) } } + +func bindError(err error) { + if err != nil { + slog.Error("failed to bind env", slog.Any("error", err)) + os.Exit(1) + } +} diff --git a/deployments/docker-compose.test.yml b/deployments/docker-compose.test.yml new file mode 100644 index 0000000..bf1e39a --- /dev/null +++ b/deployments/docker-compose.test.yml @@ -0,0 +1,45 @@ +name: "Fivemanage Lite Test" + +services: + + database-test: + image: mysql:8 + container_name: lite_database_test + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: fivemanage-lite-dev + ports: + - "3306:3306" + volumes: + - ./.docker/mysql_data:/var/lib/mysql + networks: + - fivemanage + jaeger-test: + image: jaegertracing/jaeger:2.4.0 + container_name: jaeger_test + ports: + - "16686:16686" + - "4317:4317" + - "4318:4318" + - "5778:5778" + - "9411:9411" + lite: + image: fivemanage/lite:latest + container_name: lite + environment: + - DB_DRIVER=mysql + - ADMIN_PASSWORD=password + - PORT=8080 + - DSN=root:root@tcp(database-test:3306)/fivemanage-lite-dev + - AWS_BUCKET=fivemanage-lite + - AWS_REGION=auto + ports: + - "8080:8080" + depends_on: + - database-test + networks: + - fivemanage + +networks: + fivemanage: + driver: bridge diff --git a/deployments/docker-compose.yml b/deployments/docker-compose.yml index e53f79c..8dfe165 100644 --- a/deployments/docker-compose.yml +++ b/deployments/docker-compose.yml @@ -1,19 +1,20 @@ name: "Fivemanage Lite" services: - database: - image: mysql:8 - container_name: lite_database + lite-database: + image: postgres:latest + container_name: lite-database environment: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: fivemanage-lite-dev + POSTGRES_USER: postgres + POSTGRES_PASSWORD: root + POSTGRES_DB: fivemanage-lite-dev ports: - - "3306:3306" + - "5432:5432" volumes: - - ./.docker/mysql_data:/var/lib/mysql + - ./.docker/postgres_data:/var/lib/postgresql/data networks: - fivemanage - jaeger: + lite-jaeger: image: jaegertracing/jaeger:2.4.0 container_name: jaeger ports: @@ -22,7 +23,58 @@ services: - "4318:4318" - "5778:5778" - "9411:9411" + lite-clickhouse: + image: clickhouse/clickhouse-server:latest + restart: always + container_name: lite-clickhouse + environment: + CLICKHOUSE_PASSWORD: password + ports: + - '18123:8123' + - '19000:9000' + volumes: + - clickhouse-data:/var/lib/clickhouse/ + networks: + - fivemanage + lite-kafka: + image: bitnami/kafka:4.0.0 + container_name: lite-kafka + ports: + - '9092:9092' + - '9093:9093' + - '9094:9094' + environment: + - KAFKA_CFG_NODE_ID=0 + - KAFKA_CFG_PROCESS_ROLES=controller,broker + - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 + - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://lite-kafka:9092,EXTERNAL://localhost:9094 + - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT + - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@lite-kafka:9093 + - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER + - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true + volumes: + - ./.docker/kafka:/bitnami/kafka + networks: + - fivemanage + lite-minio: + image: minio/minio:latest + container_name: lite-minio + ports: + - "9000:9000" + - "9001:9001" + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin123 + command: server /data --console-address ":9001" + volumes: + - ./.docker/minio/data:/data + networks: + - fivemanage networks: fivemanage: driver: bridge + + +volumes: + clickhouse-data: { driver: local } diff --git a/go.mod b/go.mod index 3c09db4..51baa17 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,40 @@ module github.com/fivemanage/lite -go 1.22.0 +go 1.24 require ( + github.com/ClickHouse/clickhouse-go/v2 v2.36.0 github.com/aws/aws-sdk-go-v2 v1.30.4 github.com/aws/aws-sdk-go-v2/config v1.27.31 github.com/aws/aws-sdk-go-v2/service/s3 v1.61.0 - github.com/gabriel-vasile/mimetype v1.4.3 + github.com/fatih/color v1.14.1 + github.com/gabriel-vasile/mimetype v1.4.9 github.com/go-playground/validator/v10 v10.22.1 - github.com/go-sql-driver/mysql v1.8.1 + github.com/golang-migrate/migrate/v4 v4.18.3 + github.com/huandu/go-sqlbuilder v1.35.1 github.com/joho/godotenv v1.5.1 - github.com/labstack/echo/v4 v4.11.4 + github.com/labstack/echo/v4 v4.13.4 github.com/matoous/go-nanoid/v2 v2.1.0 + github.com/segmentio/kafka-go v0.4.48 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/uptrace/bun v1.2.10 - github.com/uptrace/bun/dialect/mysqldialect v1.2.10 - github.com/uptrace/bun/dialect/pgdialect v1.2.10 + github.com/uptrace/bun v1.2.11 + github.com/uptrace/bun/dialect/pgdialect v1.2.11 github.com/uptrace/bun/driver/pgdriver v1.2.10 - golang.org/x/crypto v0.33.0 - golang.org/x/oauth2 v0.18.0 + github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 + go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.61.0 + go.opentelemetry.io/otel v1.36.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 + go.opentelemetry.io/otel/sdk v1.36.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.38.0 + golang.org/x/oauth2 v0.28.0 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect + github.com/ClickHouse/ch-go v0.66.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect @@ -40,46 +50,65 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect github.com/aws/smithy-go v1.20.4 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-faster/city v1.0.1 // indirect + github.com/go-faster/errors v0.7.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.4.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/paulmach/orb v0.11.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/log v0.6.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect - golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.72.1 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect mellium.im/sasl v0.3.2 // indirect diff --git a/go.sum b/go.sum index 9bcc796..c3790f4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,15 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/ClickHouse/ch-go v0.66.0 h1:hLslxxAVb2PHpbHr4n0d6aP8CEIpUYGMVT1Yj/Q5Img= +github.com/ClickHouse/ch-go v0.66.0/go.mod h1:noiHWyLMJAZ5wYuq3R/K0TcRhrNA8h7o1AqHX0klEhM= +github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= +github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/ClickHouse/clickhouse-go/v2 v2.36.0 h1:FJ03h8VdmBUhvR9nQEu5jRLdfG0c/HSxUjiNdOxRQww= +github.com/ClickHouse/clickhouse-go/v2 v2.36.0/go.mod h1:aijX64fKD1hAWu/zqWEmiGk7wRE8ZnpN0M3UvjsZG3I= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU= @@ -36,17 +46,48 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wI github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM= +github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -55,37 +96,64 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs= +github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/huandu/go-assert v1.1.6 h1:oaAfYxq9KNDi9qswn/6aE0EydfxSa+tWZC1KabNitYs= +github.com/huandu/go-assert v1.1.6/go.mod h1:JuIfbmYG9ykwvuxoJ3V8TB5QP+3+ajIA54Y44TmkMxs= +github.com/huandu/go-sqlbuilder v1.35.1 h1:znTuAksxq3T1rYfr3nsD4P0brWDY8qNzdZnI6+vtia4= +github.com/huandu/go-sqlbuilder v1.35.1/go.mod h1:mS0GAtrtW+XL6nM2/gXHRJax2RwSW1TraavWDFAc1JA= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= -github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= +github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE= github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -94,22 +162,45 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/segmentio/kafka-go v0.4.48 h1:9jyu9CWK4W5W+SroCe8EffbrRZVqAOkuaLd/ApID4Vs= +github.com/segmentio/kafka-go v0.4.48/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -129,6 +220,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -138,16 +230,19 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.2.10 h1:6TlxUQhGxiiv7MHjzxbV6ZNt/Im0PIQ3S45riAmbnGA= -github.com/uptrace/bun v1.2.10/go.mod h1:ww5G8h59UrOnCHmZ8O1I/4Djc7M/Z3E+EWFS2KLB6dQ= -github.com/uptrace/bun/dialect/mysqldialect v1.2.10 h1:5H6r7nQu9Cs55j8Os391Co6TXt+DGFyS7tj8yVLl6XY= -github.com/uptrace/bun/dialect/mysqldialect v1.2.10/go.mod h1:bTFIm0aeLP5IoxjdU/ZpMeQZhOMgMsRNW3BG+dLq0AY= -github.com/uptrace/bun/dialect/pgdialect v1.2.10 h1:+PAGCVyWDoAjMuAgn0+ud7fu3It8+Xvk7HQAJ5wCXMQ= -github.com/uptrace/bun/dialect/pgdialect v1.2.10/go.mod h1:hv0zsoc3PeW5fl3JeBglZT1vl2FoERY+QwvuvKsKATA= +github.com/uptrace/bun v1.2.11 h1:l9dTymsdZZAoSZ1+Qo3utms0RffgkDbIv+1UGk8N1wQ= +github.com/uptrace/bun v1.2.11/go.mod h1:ww5G8h59UrOnCHmZ8O1I/4Djc7M/Z3E+EWFS2KLB6dQ= +github.com/uptrace/bun/dialect/pgdialect v1.2.11 h1:n0VKWm1fL1dwJK5TRxYYLaRKRe14BOg2+AQgpvqzG/M= +github.com/uptrace/bun/dialect/pgdialect v1.2.11/go.mod h1:NvV1S/zwtwBnW8yhJ3XEKAQEw76SkeH7yUhfrx3W1Eo= github.com/uptrace/bun/driver/pgdriver v1.2.10 h1:AM+rkYbil7RU9SGRSWt0Gs+bsGVvnxyaAfBMSMn8CxM= github.com/uptrace/bun/driver/pgdriver v1.2.10/go.mod h1:ghwwywwNPP4xXov49gqMoUe5NoVsp09MEWPEx0QDhB0= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 h1:3/aHKUq7qaFMWxyQV0W2ryNgg8x8rVeKVA20KJUkfS0= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2/go.mod h1:Zit4b8AQXaXvA68+nzmbyDzqiyFRISyw1JiD5JqUBjw= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 h1:cj/Z6FKTTYBnstI0Lni9PA+k2foounKIPUmj1LBwNiQ= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2/go.mod h1:LDaXk90gKEC2nC7JH3Lpnhfu+2V7o/TsqomJJmqA39o= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -156,66 +251,145 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.61.0 h1:xUA/nAR2CsyadSjADVOwu6ZRpAtvB8HUqg/+bbuqhZ4= +go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.61.0/go.mod h1:/V0rmKWoHzXI2ROCfKE2PKPoo6hdlU1GRtzwzuO/3jc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/contrib/propagators/b3 v1.36.0 h1:xrAb/G80z/l5JL6XlmUMSD1i6W8vXkWrLfmkD3w/zZo= +go.opentelemetry.io/contrib/propagators/b3 v1.36.0/go.mod h1:UREJtqioFu5awNaCR8aEx7MfJROFlAWb6lPaJFbHaG0= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel/log v0.6.0 h1:nH66tr+dmEgW5y+F9LanGJUBYPrRgP4g2EkmPE3LeK8= +go.opentelemetry.io/otel/log v0.6.0/go.mod h1:KdySypjQHhP069JX0z/t26VHwa8vSwzgaKmXtIB3fJM= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= +go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 129fb59..5622fb2 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -1,13 +1,16 @@ package auth import ( + "errors" "os" + "github.com/labstack/echo/v4" "golang.org/x/oauth2" "golang.org/x/oauth2/github" ) // TODO: Move this to the authservice package +// we're not currently using this btw, soooooo whatever func NewGithubConfig() *oauth2.Config { githubClientID := os.Getenv("GITHUB_CLIENT_ID") githubClientSecret := os.Getenv("GITHUB_CLIENT_SECRET") @@ -24,3 +27,12 @@ func NewGithubConfig() *oauth2.Config { return config } + +func CurrentOrgId(c echo.Context) (string, error) { + org_id, ok := c.Get("org_id").(string) + if !ok { + return "", errors.New("org not found in context") + } + + return org_id, nil +} diff --git a/internal/auth/user.go b/internal/auth/user.go new file mode 100644 index 0000000..2e2db4e --- /dev/null +++ b/internal/auth/user.go @@ -0,0 +1,17 @@ +package auth + +import ( + "errors" + + "github.com/fivemanage/lite/api" + "github.com/labstack/echo/v4" +) + +func CurrentUser(c echo.Context) (*api.User, error) { + user, ok := c.Get("user").(*api.User) + if !ok { + return nil, errors.New("user not found in context") + } + + return user, nil +} diff --git a/internal/clickhouse/client.go b/internal/clickhouse/client.go new file mode 100644 index 0000000..477dae9 --- /dev/null +++ b/internal/clickhouse/client.go @@ -0,0 +1,119 @@ +package clickhouse + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/ClickHouse/clickhouse-go/v2/lib/driver" +) + +type Config struct { + Host string + Username string + Password string + Database string +} + +type Client struct { + conn driver.Conn +} + +type Log struct { + TraceID string + Timestamp time.Time + TeamID string + DatasetID string + Body string + Attributes map[string]string + RetentionDays int +} + +type LogField struct { + Field string + Type string +} + +func NewClient(config *Config) *Client { + c := &Client{} + + options := getClickhouseOptions(config) + conn, err := connect(options) + if err != nil { + fmt.Printf("Error connecting to ClickHouse: %v\n", err) + } + + c.conn = conn + return c +} + +func getClickhouseOptions(config *Config) *clickhouse.Options { + return &clickhouse.Options{ + Addr: []string{config.Host}, + Auth: clickhouse.Auth{ + Database: config.Database, + Username: config.Username, + Password: config.Password, + }, + // MaxOpenConns: 1000, + ClientInfo: clickhouse.ClientInfo{ + Products: []struct { + Name string + Version string + }{ + {Name: "lite-server", Version: "0.1"}, + }, + }, + Debugf: func(format string, v ...any) { + fmt.Printf(format, v) + }, + } +} + +func connect(options *clickhouse.Options) (driver.Conn, error) { + ctx := context.Background() + conn, err := clickhouse.Open(options) + if err != nil { + return nil, err + } + + if err := conn.Ping(ctx); err != nil { + var exception *clickhouse.Exception + if errors.As(err, &exception) { + fmt.Printf("Exception [%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) + } + return nil, err + } + + return conn, nil +} + +func (c *Client) BatchWriteLogRows(ctx context.Context, logs []*Log) error { + if len(logs) == 0 { + return nil + } + + batch, err := c.conn.PrepareBatch(ctx, "INSERT INTO logs (Timestamp, DatasetId, TraceId, TeamId, Body, Attributes, RetentionDays)") + if err != nil { + return fmt.Errorf("failed to prepare batch: %w", err) + } + + for _, log := range logs { + err := batch.Append( + log.Timestamp, + log.DatasetID, + log.TraceID, + log.TeamID, + log.Body, + log.Attributes, + log.RetentionDays, + ) + if err != nil { + return fmt.Errorf("failed to append log to batch: %w", err) + } + } + + return batch.Send() +} diff --git a/internal/clickhouse/migration.go b/internal/clickhouse/migration.go new file mode 100644 index 0000000..ce61b4d --- /dev/null +++ b/internal/clickhouse/migration.go @@ -0,0 +1,67 @@ +package clickhouse + +import ( + "context" + "log/slog" + "path/filepath" + "time" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/fivemanage/lite/internal/project" + "github.com/golang-migrate/migrate/v4" + clickhouseMigrate "github.com/golang-migrate/migrate/v4/database/clickhouse" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func AutoMigrate(ctx context.Context, config *Config) { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + options := getClickhouseOptions(config) + db := clickhouse.OpenDB(options) + + defer func() { + err := db.Close() + if err != nil { + slog.Error("failed to close clickhouse connection", slog.Any("error", err)) + } + }() + + if err := db.PingContext(ctx); err != nil { + slog.Error("failed to ping clickhouse", slog.Any("error", err)) + panic(err) + } + + driver, err := clickhouseMigrate.WithInstance(db, &clickhouseMigrate.Config{ + MigrationsTableEngine: "MergeTree", + MultiStatementEnabled: true, + }) + if err != nil { + slog.Error("failed to create clickhouse driver", slog.Any("error", err)) + panic(err) + } + + migrationsPath := filepath.Join(project.GetRoot(), "internal", "clickhouse", "migrations") + absPath, err := filepath.Abs(migrationsPath) + if err != nil { + slog.Error("failed to get absolute path", slog.Any("error", err)) + panic(err) + } + + sourcePath := filepath.ToSlash(absPath) + sourceURL := "file://" + sourcePath + + m, err := migrate.NewWithDatabaseInstance(sourceURL, "default", driver) + if err != nil { + slog.Error("error creating migration instance", slog.Any("error", err)) + panic(err) + } + + err = m.Up() + if err != nil && err != migrate.ErrNoChange { + slog.Error("failed to run migrations", slog.Any("error", err)) + panic(err) + } + + slog.Info("clickhouse migrations successfully completed") +} diff --git a/internal/clickhouse/migrations/000001_create_logs_table.down.sql b/internal/clickhouse/migrations/000001_create_logs_table.down.sql new file mode 100644 index 0000000..0f84b57 --- /dev/null +++ b/internal/clickhouse/migrations/000001_create_logs_table.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS logs + diff --git a/internal/clickhouse/migrations/000001_create_logs_table.up.sql b/internal/clickhouse/migrations/000001_create_logs_table.up.sql new file mode 100644 index 0000000..43663a7 --- /dev/null +++ b/internal/clickhouse/migrations/000001_create_logs_table.up.sql @@ -0,0 +1,19 @@ +CREATE TABLE IF NOT EXISTS logs +( + Timestamp DateTime, + DatasetId LowCardinality(String), + TraceId String, + TeamId LowCardinality(String), + Body String, + Attributes Map(LowCardinality(String), String), + RetentionDays UInt32 DEFAULT 90, + + INDEX idx_trace_id TraceId TYPE bloom_filter GRANULARITY 1, + INDEX idx_log_attr_key mapKeys(Attributes) TYPE bloom_filter GRANULARITY 1, + INDEX idx_log_attr_value mapValues(Attributes) TYPE bloom_filter GRANULARITY 1, + INDEX idx_body Body TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 1 +) +ENGINE = MergeTree +PARTITION BY toDate(Timestamp) +ORDER BY (TeamId, DatasetId, Timestamp) +TTL Timestamp + toIntervalDay(RetentionDays) diff --git a/internal/clickhouse/migrations/000002_create_log_attributes.down.sql b/internal/clickhouse/migrations/000002_create_log_attributes.down.sql new file mode 100644 index 0000000..f284393 --- /dev/null +++ b/internal/clickhouse/migrations/000002_create_log_attributes.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS log_attributes diff --git a/internal/clickhouse/migrations/000002_create_log_attributes.up.sql b/internal/clickhouse/migrations/000002_create_log_attributes.up.sql new file mode 100644 index 0000000..cebbfdd --- /dev/null +++ b/internal/clickhouse/migrations/000002_create_log_attributes.up.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS log_attributes +( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key LowCardinality(String), + Timestamp DateTime, + TraceId String, + Value String, + RetentionDays UInt32 +) +ENGINE = ReplacingMergeTree +ORDER BY (TeamId, DatasetId, Key, Timestamp, TraceId, Value) +TTL Timestamp + toIntervalDay(RetentionDays); diff --git a/internal/clickhouse/migrations/000003_create_log_attributes_mv.down.sql b/internal/clickhouse/migrations/000003_create_log_attributes_mv.down.sql new file mode 100644 index 0000000..681a9d4 --- /dev/null +++ b/internal/clickhouse/migrations/000003_create_log_attributes_mv.down.sql @@ -0,0 +1 @@ +DROP VIEW IF EXISTS log_attributes_mv; diff --git a/internal/clickhouse/migrations/000003_create_log_attributes_mv.up.sql b/internal/clickhouse/migrations/000003_create_log_attributes_mv.up.sql new file mode 100644 index 0000000..088e25d --- /dev/null +++ b/internal/clickhouse/migrations/000003_create_log_attributes_mv.up.sql @@ -0,0 +1,17 @@ +CREATE MATERIALIZED VIEW IF NOT EXISTS log_attributes_mv TO log_attributes ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key String, + Timestamp DateTime, + TraceId String, + Value String +) AS +SELECT + arrayJoin(Attributes).1 AS Key, + Timestamp, + TraceId, + DatasetId, + TeamId, + arrayJoin(Attributes).2 AS Value +FROM logs +WHERE Value != ''; diff --git a/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.down.sql b/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.down.sql new file mode 100644 index 0000000..681a9d4 --- /dev/null +++ b/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.down.sql @@ -0,0 +1 @@ +DROP VIEW IF EXISTS log_attributes_mv; diff --git a/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.up.sql b/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.up.sql new file mode 100644 index 0000000..951a0b9 --- /dev/null +++ b/internal/clickhouse/migrations/000004_alter_select_log_attributes_mv.up.sql @@ -0,0 +1,19 @@ +DROP VIEW IF EXISTS log_attributes_mv; + +CREATE MATERIALIZED VIEW IF NOT EXISTS log_attributes_mv TO log_attributes ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key String, + Timestamp DateTime, + TraceId String, + Value String +) AS +SELECT + arrayJoin(Attributes).1 AS Key, + Timestamp, + TraceId, + DatasetId, + TeamId, + arrayJoin(Attributes).2 AS Value +FROM logs +WHERE Value != ''; diff --git a/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.down.sql b/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.down.sql new file mode 100644 index 0000000..681a9d4 --- /dev/null +++ b/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.down.sql @@ -0,0 +1 @@ +DROP VIEW IF EXISTS log_attributes_mv; diff --git a/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.up.sql b/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.up.sql new file mode 100644 index 0000000..81247c9 --- /dev/null +++ b/internal/clickhouse/migrations/000005_add_retention_log_attributes_mv.up.sql @@ -0,0 +1,22 @@ +DROP VIEW IF EXISTS log_attributes_mv; + +CREATE MATERIALIZED VIEW IF NOT EXISTS log_attributes_mv TO log_attributes ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key String, + Timestamp DateTime, + TraceId String, + Value String, + RetentionDays UInt32 +) AS + +SELECT + arrayJoin(Attributes).1 AS Key, + Timestamp, + TraceId, + DatasetId, + TeamId, + arrayJoin(Attributes).2 AS Value, + RetentionDays +FROM logs +WHERE Value != ''; diff --git a/internal/clickhouse/migrations/000006_create_log_keys.down.sql b/internal/clickhouse/migrations/000006_create_log_keys.down.sql new file mode 100644 index 0000000..bd217ec --- /dev/null +++ b/internal/clickhouse/migrations/000006_create_log_keys.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS log_keys; diff --git a/internal/clickhouse/migrations/000006_create_log_keys.up.sql b/internal/clickhouse/migrations/000006_create_log_keys.up.sql new file mode 100644 index 0000000..f83021c --- /dev/null +++ b/internal/clickhouse/migrations/000006_create_log_keys.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS log_keys ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key LowCardinality(String), + Day DateTime, + Count UInt64, + Type LowCardinality(String), + RetentionDays UInt32 +) ENGINE = SummingMergeTree(Count) +ORDER BY (TeamId, DatasetId, Key, Day) +TTL Day + toIntervalDay(RetentionDays); diff --git a/internal/clickhouse/migrations/000007_create_log_key_values.down.sql b/internal/clickhouse/migrations/000007_create_log_key_values.down.sql new file mode 100644 index 0000000..592f883 --- /dev/null +++ b/internal/clickhouse/migrations/000007_create_log_key_values.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS log_key_values; diff --git a/internal/clickhouse/migrations/000007_create_log_key_values.up.sql b/internal/clickhouse/migrations/000007_create_log_key_values.up.sql new file mode 100644 index 0000000..14f0dd6 --- /dev/null +++ b/internal/clickhouse/migrations/000007_create_log_key_values.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS log_key_values ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key LowCardinality(String), + Day DateTime, + Value String, + Count UInt64, + RetentionDays UInt32 +) ENGINE = SummingMergeTree(Count) +ORDER BY (TeamId, DatasetId, Key, Day, Value) +TTL Day + toIntervalDay(RetentionDays) diff --git a/internal/clickhouse/migrations/000008_create_log_key_values_mv.down.sql b/internal/clickhouse/migrations/000008_create_log_key_values_mv.down.sql new file mode 100644 index 0000000..62755da --- /dev/null +++ b/internal/clickhouse/migrations/000008_create_log_key_values_mv.down.sql @@ -0,0 +1 @@ +DROP VIEW IF EXISTS log_key_values_mv; diff --git a/internal/clickhouse/migrations/000008_create_log_key_values_mv.up.sql b/internal/clickhouse/migrations/000008_create_log_key_values_mv.up.sql new file mode 100644 index 0000000..d8bcd26 --- /dev/null +++ b/internal/clickhouse/migrations/000008_create_log_key_values_mv.up.sql @@ -0,0 +1,24 @@ +CREATE MATERIALIZED VIEW IF NOT EXISTS log_key_values_mv TO log_key_values ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key LowCardinality(String), + Day DateTime, + Value String, + Count UInt64, + RetentionDays UInt32 +) AS +SELECT + TeamId, + DatasetId, + Key, + toStartOfDay(Timestamp) AS Day, + Value, + count() AS Count, + RetentionDays +FROM log_attributes +GROUP BY TeamId, + DatasetId, + Key, + Day, + Value, + RetentionDays; diff --git a/internal/clickhouse/migrations/000009_create_log_keys_mv.down.sql b/internal/clickhouse/migrations/000009_create_log_keys_mv.down.sql new file mode 100644 index 0000000..960aa24 --- /dev/null +++ b/internal/clickhouse/migrations/000009_create_log_keys_mv.down.sql @@ -0,0 +1 @@ +DROP VIEW IF EXISTS log_keys_mv; diff --git a/internal/clickhouse/migrations/000009_create_log_keys_mv.up.sql b/internal/clickhouse/migrations/000009_create_log_keys_mv.up.sql new file mode 100644 index 0000000..bb97b16 --- /dev/null +++ b/internal/clickhouse/migrations/000009_create_log_keys_mv.up.sql @@ -0,0 +1,26 @@ +CREATE MATERIALIZED VIEW IF NOT EXISTS log_keys_mv TO log_keys ( + TeamId LowCardinality(String), + DatasetId LowCardinality(String), + Key LowCardinality(String), + Day DateTime, + Count UInt64, + Type String, + RetentionDays UInt32 +) AS +SELECT TeamId, + DatasetId, + Key, + Day, + sum(Count) AS Count, + if( + isNull(toFloat64OrNull(Value)), + 'String', + 'Numeric' + ) AS Type, + max(RetentionDays) as RetentionDays +FROM log_key_values +GROUP BY TeamId, + DatasetId, + Key, + Day, + isNull(toFloat64OrNull(Value)); diff --git a/internal/clickhouse/migrations/000010_add_otel_trace_table.down.sql b/internal/clickhouse/migrations/000010_add_otel_trace_table.down.sql new file mode 100644 index 0000000..86c4658 --- /dev/null +++ b/internal/clickhouse/migrations/000010_add_otel_trace_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS otel_traces diff --git a/internal/clickhouse/migrations/000010_add_otel_trace_table.up.sql b/internal/clickhouse/migrations/000010_add_otel_trace_table.up.sql new file mode 100644 index 0000000..d0302bf --- /dev/null +++ b/internal/clickhouse/migrations/000010_add_otel_trace_table.up.sql @@ -0,0 +1,43 @@ +CREATE TABLE default.otel_traces +( + `TeamId` LowCardinality(String) DEFAULT ResourceAttributes['team.id'], + `DatasetId` LowCardinality(String) DEFAULT ResourceAttributes['dataset.id'], + + `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)), + `TraceId` String CODEC(ZSTD(1)), + `SpanId` String CODEC(ZSTD(1)), + `ParentSpanId` String CODEC(ZSTD(1)), + `TraceState` String CODEC(ZSTD(1)), + `SpanName` LowCardinality(String) CODEC(ZSTD(1)), + `SpanKind` LowCardinality(String) CODEC(ZSTD(1)), + `ServiceName` LowCardinality(String) CODEC(ZSTD(1)), + + `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)), + `ScopeName` String CODEC(ZSTD(1)), + `ScopeVersion` String CODEC(ZSTD(1)), + `SpanAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)), + + `Duration` Int64 CODEC(ZSTD(1)), + `StatusCode` LowCardinality(String) CODEC(ZSTD(1)), + `StatusMessage` String CODEC(ZSTD(1)), + + `Events.Timestamp` Array(DateTime64(9)) CODEC(ZSTD(1)), + `Events.Name` Array(LowCardinality(String)) CODEC(ZSTD(1)), + `Events.Attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)), + + `Links.TraceId` Array(String) CODEC(ZSTD(1)), + `Links.SpanId` Array(String) CODEC(ZSTD(1)), + `Links.TraceState` Array(String) CODEC(ZSTD(1)), + `Links.Attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)), + + INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_span_attr_key mapKeys(SpanAttributes) TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_span_attr_value mapValues(SpanAttributes) TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_duration Duration TYPE minmax GRANULARITY 1 +) +ENGINE = MergeTree +PARTITION BY toDate(Timestamp) +ORDER BY (TeamId, DatasetId, ServiceName, toUnixTimestamp(Timestamp), TraceId) +TTL toDateTime(Timestamp) + toIntervalDay(30) +SETTINGS ttl_only_drop_parts = 1; diff --git a/internal/clickhouse/query.go b/internal/clickhouse/query.go new file mode 100644 index 0000000..fa5bc1e --- /dev/null +++ b/internal/clickhouse/query.go @@ -0,0 +1,105 @@ +package clickhouse + +import ( + "context" + "fmt" + "time" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/pkg/querybuilder" +) + +func (c *Client) QueryLogs(ctx context.Context, organizationID, datasetID string, startTime, endTime time.Time, filter api.DatasetFilter, cursor int) ([]api.DatasetLog, error) { + qb := querybuilder.New().Filter(filter).WithDateRange(startTime, endTime) + query, args := qb.Build(organizationID, datasetID) + + fmt.Println(query) + + rows, err := c.conn.Query(ctx, query, args...) + if err != nil { + return nil, err + } + + defer func() { + if err := rows.Close(); err != nil { + fmt.Println(err) + } + }() + + var logs []api.DatasetLog + for rows.Next() { + var ( + Timestamp time.Time + DatasetId string + TraceId string + TeamId string + Body string + Attributes map[string]string + RetentionDays uint32 + ) + if err := rows.Scan(&Timestamp, &DatasetId, &TraceId, &TeamId, &Body, &Attributes, &RetentionDays); err != nil { + return nil, err + } + logs = append(logs, api.DatasetLog{ + Timestamp: Timestamp, + DatasetId: DatasetId, + TraceId: TraceId, + TeamId: TeamId, + Body: Body, + Attributes: Attributes, + }) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return logs, nil +} + +func (r *Client) QueryLogFields(ctx context.Context, organizationID, datasetID string) ([]LogField, error) { + chCtx := clickhouse.Context(ctx, clickhouse.WithParameters(clickhouse.Parameters{ + "TeamId": organizationID, + "DatasetId": datasetID, + })) + + query, err := r.conn.Query(chCtx, "SELECT DISTINCT Key, Type FROM log_keys WHERE TeamId = {TeamId:String} AND DatasetId = {DatasetId:String}") + if err != nil { + fmt.Println(err) + return nil, err + } + + defer func() { + if err := query.Close(); err != nil { + fmt.Println(err) + } + }() + + var fields []LogField + + for query.Next() { + var ( + field string + fieldType string + ) + + err := query.Scan(&field, &fieldType) + if err != nil { + fmt.Println(err) + return nil, err + } + + fields = append(fields, LogField{Field: field, Type: fieldType}) + } + + if err := query.Err(); err != nil { + return nil, err + } + + return fields, nil +} + +func (r *Client) QueryTotalLogs(ctx context.Context, organizationID, datasetID string) (int, error) { + return 0, nil +} diff --git a/internal/crypt/hmac.go b/internal/crypt/hmac.go new file mode 100644 index 0000000..a79a15f --- /dev/null +++ b/internal/crypt/hmac.go @@ -0,0 +1,14 @@ +package crypt + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" +) + +// used for generating a hash of the api token +func ComputeHMAC(secret, message string) string { + mac := hmac.New(sha256.New, []byte(secret)) + mac.Write([]byte(message)) + return hex.EncodeToString(mac.Sum(nil)) +} \ No newline at end of file diff --git a/internal/crypt/nano.go b/internal/crypt/nano.go index 665c2aa..5033fb1 100644 --- a/internal/crypt/nano.go +++ b/internal/crypt/nano.go @@ -25,3 +25,11 @@ func GenerateFilename() (string, error) { } return id, nil } + +func GeneratePrimaryKey() (string, error) { + id, err := gonanoid.Generate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 16) + if err != nil { + return "", nil + } + return id, nil +} diff --git a/internal/database/database.go b/internal/database/database.go index ba732c5..8b9cb11 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,7 +1,6 @@ package database import ( - "fmt" "time" "github.com/uptrace/bun" @@ -28,55 +27,67 @@ type Session struct { User *User `bun:"rel:belongs-to,join:user_id=id"` } +// nanoid to keep the ID more url friendly +// might switch to uuid later type Organization struct { bun.BaseModel `bun:"table:organization"` - ID int64 `bun:"id,pk,autoincrement"` - OwnerID string `bun:"owner_Id"` + ID string `bun:"id,pk"` Name string `bun:"name"` - User *User `bun:"rel:belongs-to,join:owner_id=id"` } type OrganizationMember struct { bun.BaseModel `bun:"table:organization_member"` ID int64 `bun:"id,pk,autoincrement"` - UserID int `bun:"user_id"` - OrganizationID int `bun:"organization_id"` + Role string `bun:"role"` + UserID int64 `bun:"user_id"` + OrganizationID string `bun:"organization_id"` User *User `bun:"rel:belongs-to,join:user_id=id"` Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"` } type Token struct { - bun.BaseModel `bun:"table:token"` - ID int64 `bun:"id,pk,autoincrement"` - TokenHash string `bun:"token_hash"` - Identifier string `bun:"identifier"` - UserID int `bun:"user_id"` - User *User `bun:"rel:belongs-to,join:user_id=id"` + bun.BaseModel `bun:"table:token"` + ID int64 `bun:"id,pk,autoincrement"` + TokenHash string `bun:"token_hash"` + Identifier string `bun:"identifier"` + UserID int `bun:"user_id"` + User *User `bun:"rel:belongs-to,join:user_id=id"` + OrganizationID string `bun:"organization_id"` + Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"` + CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"` } -type File struct { - bun.BaseModel `bun:"table:file"` +// nanoid to keep the ID more url friendly +// might switch to uuid later +type Asset struct { + bun.BaseModel `bun:"table:asset"` + ID string `bun:"id,pk"` Key string `bun:"key"` Size int64 `bun:"size"` Type string `bun:"type"` - OrganizationID int64 `bun:"organization_id"` + CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp"` + OrganizationID string `bun:"organization_id"` + Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"` +} + +type Dataset struct { + bun.BaseModel `bun:"table:dataset"` + ID string `bun:"id,pk"` + Name string `bun:"name"` + Description string `bun:"description"` + RetentionDays int `bun:"retention_days"` + CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp"` + OrganizationID string `bun:"organization_id"` Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"` } type Store interface { - Connect() *bun.DB + Connect(dsn string) *bun.DB } // TODO: Return error if driver is not supported -func New(driver string, dsn string) Store { - fmt.Println("option", driver) - - switch driver { - case "mysql": - return &MySQL{} - case "pg", "postgresql": - return &PostgreSQL{} - default: - return nil - } +func New(driver string) Store { + return &PostgreSQL{} } diff --git a/internal/database/mysql.go b/internal/database/mysql.go deleted file mode 100644 index 09a4a13..0000000 --- a/internal/database/mysql.go +++ /dev/null @@ -1,21 +0,0 @@ -package database - -import ( - "database/sql" - "log" - - _ "github.com/go-sql-driver/mysql" - "github.com/uptrace/bun" - "github.com/uptrace/bun/dialect/mysqldialect" -) - -type MySQL struct{} - -func (r *MySQL) Connect() *bun.DB { - sqldb, err := sql.Open("mysql", "root:root@tcp(localhost)/fivemanage-lite-dev") - if err != nil { - log.Fatalf("failed to open mysql connection: %v", err) - } - - return bun.NewDB(sqldb, mysqldialect.New()) -} diff --git a/internal/database/pg.go b/internal/database/pg.go index 998c5fb..31f3439 100644 --- a/internal/database/pg.go +++ b/internal/database/pg.go @@ -2,6 +2,8 @@ package database import ( "database/sql" + "log/slog" + "time" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect" @@ -10,10 +12,31 @@ import ( type PostgreSQL struct{} -func (r *PostgreSQL) Connect() *bun.DB { - dsn := "postgres://postgres:@localhost:5432/test?sslmode=disable" - // dsn := "unix://user:pass@dbname/var/run/postgresql/.s.PGSQL.5432" - sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) +func (r *PostgreSQL) Connect(dsn string) *bun.DB { + var sqldb *sql.DB + var err error + + maxRetries := 3 + retryWaitTime := 5 * time.Second + + for i := range maxRetries { + sqldb = sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) + + err = sqldb.Ping() + if err == nil { + slog.Info("successfully connected to the PostgreSQL database") + break + } + + slog.Warn("could not connect to the database. Retrying in 5s...", slog.Any("error", err)) + + time.Sleep(retryWaitTime) + + if i == maxRetries-1 { + slog.Error("failed to connect to the database after multiple attempts", slog.Any("error", err)) + } + + } return bun.NewDB(sqldb, pgdialect.New()) } diff --git a/internal/database/query/dataset/dataset_query.go b/internal/database/query/dataset/dataset_query.go new file mode 100644 index 0000000..d42eef8 --- /dev/null +++ b/internal/database/query/dataset/dataset_query.go @@ -0,0 +1,56 @@ +package dataset + +import ( + "context" + + "github.com/fivemanage/lite/internal/database" + "github.com/uptrace/bun" +) + +func Insert(ctx context.Context, db *bun.DB, dataset *database.Dataset) error { + _, err := db.NewInsert().Model(dataset).Exec(ctx) + if err != nil { + return err + } + + return nil +} + +func List(ctx context.Context, db *bun.DB, orgID string) ([]database.Dataset, error) { + var datasets []database.Dataset + + err := db.NewSelect().Model(&datasets). + Where("organization_id = ?", orgID). + Scan(ctx) + if err != nil { + return nil, err + } + + return datasets, nil +} + +func SelectByName(ctx context.Context, db *bun.DB, orgID string, datasetName string) (*database.Dataset, error) { + var dataset database.Dataset + + err := db.NewSelect().Model(&dataset). + Where("organization_id = ?", orgID). + Where("name = ?", datasetName). + Scan(ctx) + if err != nil { + return nil, err + } + + return &dataset, nil +} + +func Delete(ctx context.Context, db *bun.DB, orgID string, datasetID string) error { + _, err := db.NewDelete().Model((*database.Dataset)(nil)). + Where("organization_id = ?", orgID). + Where("id = ?", datasetID). + Exec(ctx) + if err != nil { + return err + } + + return nil +} diff --git a/internal/database/query/file/file_query.go b/internal/database/query/file/file_query.go index 0f7af3a..b77879f 100644 --- a/internal/database/query/file/file_query.go +++ b/internal/database/query/file/file_query.go @@ -2,13 +2,22 @@ package filequery import ( "context" + "database/sql" + "errors" "github.com/fivemanage/lite/internal/database" "github.com/uptrace/bun" ) -func Create(ctx context.Context, db *bun.DB, file *database.File) (bun.Tx, error) { +// todo: rename to Insert +func Create(ctx context.Context, db *bun.DB, file *database.Asset) (bun.Tx, error) { + var err error + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return tx, err + } + _, err = tx.NewInsert().Model(file).Exec(ctx) if err != nil { return tx, err @@ -16,3 +25,42 @@ func Create(ctx context.Context, db *bun.DB, file *database.File) (bun.Tx, error return tx, nil } + +func FindStorageFiles(ctx context.Context, db *bun.DB, organizationID, search string) ([]*database.Asset, error) { + var files []*database.Asset + + // no, this does not scale at all, but...soonTM + // we are also missing indexes I need to create migrations for + sb := db.NewSelect(). + Model(&files). + Where("organization_id = ?", organizationID). + Order("created_at DESC") + + if search != "" { + // not my proudest moment + sb = sb.Where("name LIKE ?", "%"+search+"%") + } + + err := sb.Scan(ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + + return nil, err + } + + return files, nil +} + +func FindTotalStorageCount(ctx context.Context, db *bun.DB, organizationID string) (int, error) { + count, err := db.NewSelect(). + Model((*database.Asset)(nil)). + Where("organization_id = ?", organizationID). + Count(ctx) + if err != nil { + return 0, err + } + + return count, nil +} diff --git a/internal/database/query/organization/organization_query.go b/internal/database/query/organization/organization_query.go new file mode 100644 index 0000000..bac3644 --- /dev/null +++ b/internal/database/query/organization/organization_query.go @@ -0,0 +1,57 @@ +package organizationquery + +import ( + "context" + + "github.com/fivemanage/lite/internal/database" + "github.com/uptrace/bun" +) + +func Create(ctx context.Context, db *bun.DB, organization *database.Organization) (bun.Tx, error) { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return tx, err + } + + _, err = tx.NewInsert().Model(organization).Exec(ctx) + if err != nil { + return tx, err + } + + return tx, nil +} + +func Find(ctx context.Context, db *bun.DB, ID string) (*database.Organization, error) { + organization := new(database.Organization) + err := db.NewSelect().Model(organization).Where("id = ?", ID).Scan(ctx) + if err != nil { + return nil, err + } + + return organization, nil +} + +func List(ctx context.Context, db *bun.DB) ([]database.Organization, error) { + var organizations []database.Organization + + err := db.NewSelect().Model(&organizations).Scan(ctx) + if err != nil { + return nil, err + } + + return organizations, nil +} + +func CreateMember(ctx context.Context, db *bun.DB, member *database.OrganizationMember) (bun.Tx, error) { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return tx, err + } + + _, err = tx.NewInsert().Model(member).Exec(ctx) + if err != nil { + return tx, err + } + + return tx, nil +} diff --git a/internal/database/query/token/token_query.go b/internal/database/query/token/token_query.go index 5234bf5..76dbb1e 100644 --- a/internal/database/query/token/token_query.go +++ b/internal/database/query/token/token_query.go @@ -7,6 +7,17 @@ import ( "github.com/uptrace/bun" ) +func SelectByHash(ctx context.Context, db *bun.DB, tokenHash string) (*database.Token, error) { + var token database.Token + + err := db.NewSelect().Model(&token).Where("token_hash = ?", tokenHash).Scan(ctx) + if err != nil { + return nil, err + } + + return &token, nil +} + func Create(ctx context.Context, db *bun.DB, token *database.Token) error { _, err := db.NewInsert().Model(token).Exec(ctx) if err != nil { diff --git a/internal/http/dist/index.html b/internal/http/dist/index.html new file mode 100644 index 0000000..78df89c --- /dev/null +++ b/internal/http/dist/index.html @@ -0,0 +1,10 @@ + + + + + Fivemanage + + + No embeddable content found. + + diff --git a/internal/http/httputil/formdata.go b/internal/http/httputil/formdata.go index 4d55f5e..cb9349b 100644 --- a/internal/http/httputil/formdata.go +++ b/internal/http/httputil/formdata.go @@ -1,13 +1,20 @@ package httputil import ( + "log" "mime/multipart" "net/http" + + "github.com/uptrace/opentelemetry-go-extra/otelzap" + "go.uber.org/zap" ) func File(r *http.Request, key string) (file multipart.File, fileHeader *multipart.FileHeader, err error) { err = r.ParseMultipartForm(32 << 20) if err != nil { + // logrus.WithError(err).Error("failed to parse multipart form") + otelzap.S().Error("failed to parse mutlipart form", zap.Error(err)) + log.Println("failed to parse stuff", err.Error()) return nil, nil, err } diff --git a/internal/http/httputil/httputil.go b/internal/http/httputil/httputil.go index 76f2265..03873b9 100644 --- a/internal/http/httputil/httputil.go +++ b/internal/http/httputil/httputil.go @@ -5,9 +5,27 @@ type response struct { Data any `json:"data"` } +type errorResponse struct { + Status string `json:"status"` + Data errorData `json:"data"` +} + +type errorData struct { + Message string `json:"message"` +} + func Response(data any) *response { return &response{ Status: "ok", Data: data, } } + +func ErrorResponse(message string) *errorResponse { + return &errorResponse{ + Status: "error", + Data: errorData{ + Message: message, + }, + } +} diff --git a/internal/http/httputil/mime.go b/internal/http/httputil/mime.go new file mode 100644 index 0000000..b8a78d2 --- /dev/null +++ b/internal/http/httputil/mime.go @@ -0,0 +1,29 @@ +package httputil + +import ( + "fmt" + "mime/multipart" + "strings" + + "github.com/gabriel-vasile/mimetype" +) + +func GetMimeDetails(fileHeader *multipart.FileHeader, file multipart.File) (string, string, string, error) { + var err error + var fileType string + + // Get the file type from the header + buf := make([]byte, fileHeader.Size) + _, err = file.Read(buf) + if err != nil { + return "", "", "", fmt.Errorf("error reading file: %w", err) + } + + mime := mimetype.Detect(buf) + mimeType := mime.String() + extension := mime.Extension() + + fileType = strings.Split(mime.String(), "/")[0] + + return mimeType, extension, fileType, nil +} diff --git a/internal/http/internalapi/admin_group.go b/internal/http/internalapi/admin_group.go new file mode 100644 index 0000000..78734fa --- /dev/null +++ b/internal/http/internalapi/admin_group.go @@ -0,0 +1,20 @@ +package internalapi + +import ( + "fmt" + + "github.com/fivemanage/lite/internal/service/admin" + "github.com/labstack/echo/v4" +) + +func registerAdminApi(group *echo.Group, adminService *admin.Service) { + group.POST("/:organizationId/admin/member", func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + + fmt.Println("orgID", orgID) + fmt.Println("ctx", ctx) + + return nil + }) +} diff --git a/internal/http/internalapi/auth_group.go b/internal/http/internalapi/auth_group.go index bd92da8..b385d70 100644 --- a/internal/http/internalapi/auth_group.go +++ b/internal/http/internalapi/auth_group.go @@ -69,4 +69,29 @@ func registerAuthApi(group *echo.Group, authservice *auth.Auth) { c.SetCookie(sessionCookie) return c.Redirect(http.StatusSeeOther, "/app") }) + + group.POST("/auth/logout", func(c echo.Context) error { + ctx := context.Background() + + sessionCookie, err := c.Cookie("fmlite_session") + if err != nil { + return c.JSON(http.StatusOK, echo.Map{ + "message": "Already logged out", + }) + } + + err = authservice.LogoutUser(ctx, sessionCookie.Value) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{ + "error": "Failed to logout", + }) + } + + logoutCookie := authservice.CreateLogoutCookie() + c.SetCookie(logoutCookie) + + return c.JSON(http.StatusOK, echo.Map{ + "message": "Logout successful", + }) + }) } diff --git a/internal/http/internalapi/dataset_group.go b/internal/http/internalapi/dataset_group.go new file mode 100644 index 0000000..03742a7 --- /dev/null +++ b/internal/http/internalapi/dataset_group.go @@ -0,0 +1,81 @@ +package internalapi + +import ( + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/http/httputil" + "github.com/fivemanage/lite/internal/http/validator" + "github.com/fivemanage/lite/internal/service/dataset" + "github.com/labstack/echo/v4" +) + +func registerDatasetApi(group *echo.Group, datasetService *dataset.Service) { + group.POST("/:organizationId/dataset", func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + + var data api.Dataset + if err := validator.BindAndValidate(c, &data); err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + // the only reason why we copy the data is because we + // we add the ID to the dataobject + dataset, err := datasetService.Create(ctx, orgID, data) + if err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + return c.JSON(200, httputil.Response(dataset)) + }) + + group.GET("/:organizationId/dataset", func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + + datasets, err := datasetService.List(ctx, orgID) + if err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + return c.JSON(200, httputil.Response(datasets)) + }) + + group.GET("/:organizationId/dataset/:datasetId/fields", func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + datasetID := c.Param("datasetId") + + fields, err := datasetService.ListFields(ctx, orgID, datasetID) + if err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + return c.JSON(200, httputil.Response(fields)) + }) + + group.POST("/:organizationId/dataset/:datasetId/logs", func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + datasetID := c.Param("datasetId") + + var data api.ListLogsSchema + if err := validator.BindAndValidate(c, &data); err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + logs, err := datasetService.QueryLogs(ctx, orgID, datasetID, data.FromDate, data.ToDate, data.Filter, data.Cursor) + if err != nil { + return echo.NewHTTPError(500, httputil.ErrorResponse(err.Error())) + } + + var response api.DatasetLogsResponse + response.Data = logs + if response.Data == nil { + response.Data = make([]api.DatasetLog, 0) + } + + response.Meta.TotalRowCount = len(logs) + + return c.JSON(200, httputil.Response(response)) + }) +} diff --git a/internal/http/internalapi/internal_api.go b/internal/http/internalapi/internal_api.go index e1f17c7..1de00fd 100644 --- a/internal/http/internalapi/internal_api.go +++ b/internal/http/internalapi/internal_api.go @@ -1,12 +1,28 @@ package internalapi import ( + "github.com/fivemanage/lite/internal/http/middleware" "github.com/fivemanage/lite/internal/service/auth" + "github.com/fivemanage/lite/internal/service/dataset" + "github.com/fivemanage/lite/internal/service/file" + "github.com/fivemanage/lite/internal/service/organization" "github.com/fivemanage/lite/internal/service/token" "github.com/labstack/echo/v4" ) -func Add(group *echo.Group, authservice *auth.Auth, tokenservice *token.Service) { - registerAuthApi(group, authservice) - registerTokensApi(group, tokenservice) +func Add( + group *echo.Group, + authService *auth.Auth, + tokenService *token.Service, + organizationService *organization.Service, + fileService *file.Service, + datasetService *dataset.Service, +) { + group.Use(middleware.Session(authService)) + + registerAuthApi(group, authService) + registerTokensApi(group, tokenService) + registerOrganizationApi(group, organizationService) + registerStorageApi(group, fileService) + registerDatasetApi(group, datasetService) } diff --git a/internal/http/internalapi/organization_group.go b/internal/http/internalapi/organization_group.go new file mode 100644 index 0000000..3ca8032 --- /dev/null +++ b/internal/http/internalapi/organization_group.go @@ -0,0 +1,49 @@ +package internalapi + +import ( + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/auth" + "github.com/fivemanage/lite/internal/http/httputil" + "github.com/fivemanage/lite/internal/service/organization" + "github.com/labstack/echo/v4" +) + +func registerOrganizationApi(group *echo.Group, organizationService *organization.Service) { + group.POST("/organization", func(c echo.Context) error { + var data api.CreateOrganizationRequest + if err := c.Bind(&data); err != nil { + return echo.NewHTTPError(400, err) + } + + currentUser, err := auth.CurrentUser(c) + if err != nil { + return echo.NewHTTPError(401, err) + } + + organization, err := organizationService.CreateOrganization(c.Request().Context(), &data, currentUser.ID) + if err != nil { + return echo.NewHTTPError(500, err) + } + + return c.JSON(200, organization) + }) + + group.GET("/organization", func(c echo.Context) error { + organizations, err := organizationService.ListOrganizations(c.Request().Context()) + if err != nil { + return echo.NewHTTPError(500, err) + } + + return c.JSON(200, httputil.Response(organizations)) + }) + + group.GET("/organization/:id", func(c echo.Context) error { + id := c.Param("id") + organization, err := organizationService.FindOrganizationByID(c.Request().Context(), id) + if err != nil { + return echo.NewHTTPError(500, err) + } + + return c.JSON(200, httputil.Response(organization)) + }) +} diff --git a/internal/http/internalapi/storage_group.go b/internal/http/internalapi/storage_group.go new file mode 100644 index 0000000..96cad29 --- /dev/null +++ b/internal/http/internalapi/storage_group.go @@ -0,0 +1,68 @@ +package internalapi + +import ( + "errors" + "net/http" + + "github.com/fivemanage/lite/internal/http/httputil" + "github.com/fivemanage/lite/internal/service/file" + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" +) + +func registerStorageApi(group *echo.Group, fileService *file.Service) { + group.GET("/storage/:organizationId", func(c echo.Context) error { + ctx := c.Request().Context() + + organizationID := c.Param("organizationId") + search := c.QueryParam("search") + + assetData, err := fileService.ListStorageFiles(ctx, organizationID, search) + if err != nil { + if errors.Is(err, file.ListStorageError{}) { + return echo.NewHTTPError(http.StatusInternalServerError, + httputil.ErrorResponse("Failed to list storage files"), + ) + } + + return echo.NewHTTPError(http.StatusInternalServerError, httputil.ErrorResponse(err.Error())) + } + + return c.JSON(200, httputil.Response(assetData)) + }) + + group.POST("/storage/:organizationId/upload", func(c echo.Context) error { + ctx := c.Request().Context() + + organizationID := c.Param("organizationId") + + formFile, header, err := httputil.File(c.Request(), "file") + if err != nil { + logrus.WithField("organizationId", organizationID).Error("failed to get file from request") + + return echo.NewHTTPError(http.StatusInternalServerError, + httputil.ErrorResponse("Failed to get file from request"), + ) + } + + err = fileService.CreateStorageFile( + ctx, + organizationID, + formFile, + header, + ) + if err != nil { + logrus.WithError(err).WithField("organizationId", organizationID).Error("failed to create file") + + if errors.Is(err, file.UploadStorageError{}) { + return echo.NewHTTPError(http.StatusInternalServerError, + httputil.ErrorResponse("Failed to upload storage file"), + ) + } + + return echo.NewHTTPError(http.StatusInternalServerError, httputil.ErrorResponse(err.Error())) + } + + return c.JSON(http.StatusOK, httputil.Response("File uploaded successfully")) + }) +} diff --git a/internal/http/internalapi/tokens_group.go b/internal/http/internalapi/tokens_group.go index 8344f71..c8e6f98 100644 --- a/internal/http/internalapi/tokens_group.go +++ b/internal/http/internalapi/tokens_group.go @@ -13,17 +13,25 @@ import ( ) func registerTokensApi(group *echo.Group, tokenService *token.Service) { - group.POST("/token", func(c echo.Context) error { + group.POST("/:organizationId/token", func(c echo.Context) error { // this might not work well for telemetry ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second) defer cancel() + // we caaaan put this in the request body as well + // and if we keep it like this, we should create some org middleware + // cuz this can be anything right now....fuck it tho + // it should also probably be :organizationId/token, not the other way around + // which again makes it easier to use a middleware + organizationID := c.Param("organizationId") + var data api.CreateTokenRequest if err := validator.BindAndValidate(c, &data); err != nil { logrus.WithError(err).Error("failed to bind and validate token") return echo.NewHTTPError(500, err) } + data.OrganizationID = organizationID apiToken, err := tokenService.CreateToken(ctx, &data) if err != nil { return echo.NewHTTPError(500, err) diff --git a/internal/http/mime.go b/internal/http/middleware/mime.go similarity index 85% rename from internal/http/mime.go rename to internal/http/middleware/mime.go index 1dad13b..8131c2e 100644 --- a/internal/http/mime.go +++ b/internal/http/middleware/mime.go @@ -1,4 +1,4 @@ -package http +package middleware import ( "fmt" @@ -9,7 +9,7 @@ import ( "github.com/labstack/echo/v4" ) -var whitelistedImageMIME = []string{ +var WhitelistedImageMIME = []string{ "image/png", "image/apng", "image/webp", @@ -17,7 +17,7 @@ var whitelistedImageMIME = []string{ "image/gif", } -var whitelistedVideoMIME = []string{ +var WhitelistedVideoMIME = []string{ "video/ogg", "video/mp4", "video/mpeg", @@ -26,7 +26,7 @@ var whitelistedVideoMIME = []string{ "application/octet-stream", } -var whitelistedAudioMIME = []string{ +var WhitelistedAudioMIME = []string{ "audio/mpeg", "audio/mp3", "audio/ogg", @@ -36,7 +36,7 @@ var whitelistedAudioMIME = []string{ "application/octet-stream", } -func (r *Server) validateMimeMiddleware(fileKey string, whitelistedTypes []string) echo.MiddlewareFunc { +func ValidateMime(fileKey string, whitelistedTypes []string) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(ctx echo.Context) error { var err error diff --git a/internal/http/middleware/session.go b/internal/http/middleware/session.go new file mode 100644 index 0000000..77a46f3 --- /dev/null +++ b/internal/http/middleware/session.go @@ -0,0 +1,51 @@ +package middleware + +import ( + "log/slog" + "net/http" + "strings" + + "github.com/fivemanage/lite/internal/service/auth" + "github.com/labstack/echo/v4" +) + +func Session(authService *auth.Auth) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ctx := c.Request().Context() + orgID := c.Param("organizationId") + + if strings.HasPrefix(c.Request().URL.Path, "/api/dash/auth") { + return next(c) + } + + sessionCookie, err := c.Cookie("fmlite_session") + if err != nil { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": "session required"}) + } + + user, err := authService.UserBySession(ctx, sessionCookie.Value) + if err != nil { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid session"}) + } + + if len(orgID) > 0 { + isMember, err := authService.IsOrganizationMember(ctx, int64(user.ID), orgID) + if err != nil { + slog.Error("failed to check organization membership", "error", err, "userID", user.ID, "orgID", orgID) + // we need a better error here + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "authorization check failed"}) + } + + if !isMember { + // and here + return c.JSON(http.StatusForbidden, map[string]string{"error": "access denied to organization"}) + } + } + + c.Set("user", user) + c.Set("org_id", orgID) + return next(c) + } + } +} diff --git a/internal/http/middleware/token_auth.go b/internal/http/middleware/token_auth.go new file mode 100644 index 0000000..1ed9d4a --- /dev/null +++ b/internal/http/middleware/token_auth.go @@ -0,0 +1,60 @@ +package middleware + +import ( + "fmt" + "time" + + "github.com/fivemanage/lite/internal/database" + "github.com/fivemanage/lite/internal/service/token" + "github.com/fivemanage/lite/pkg/cache" + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" +) + +func TokenAuth(tokenService *token.Service, memcache *cache.Cache) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + var err error + ctx := c.Request().Context() + + token := c.Request().Header.Get("Authorization") + if token == "" { + return c.JSON(401, echo.Map{ + "error": "Unauthorized: No token provided", + }) + } + + var tokenData *database.Token + + key := fmt.Sprintf(cache.TokenCacheKey, token) + tokenCacheData, found := memcache.Get(key) + if !found { + tokenData, err = tokenService.GetToken(ctx, token) + if err != nil { + logrus.WithField("error", err).Error("failed to get token from service") + return c.JSON(401, echo.Map{ + "error": "Unauthorized: Invalid token", + }) + } + + memcache.Set(key, tokenData, 5*time.Minute) + + c.Set("org_id", tokenData.OrganizationID) + c.Set("token_data", tokenData) + return next(c) + } + + tokenData, ok := tokenCacheData.(*database.Token) + if !ok { + logrus.Error("failed to assert token data from cache") + return c.JSON(500, echo.Map{ + "error": "Internal server error", + }) + } + + c.Set("org_id", tokenData.OrganizationID) + c.Set("token_data", tokenData) + return next(c) + } + } +} diff --git a/internal/http/publicapi/logs_group.go b/internal/http/publicapi/logs_group.go new file mode 100644 index 0000000..1fd3965 --- /dev/null +++ b/internal/http/publicapi/logs_group.go @@ -0,0 +1,39 @@ +package publicapi + +import ( + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/auth" + "github.com/fivemanage/lite/internal/http/middleware" + "github.com/fivemanage/lite/internal/service/log" + "github.com/fivemanage/lite/internal/service/token" + "github.com/fivemanage/lite/pkg/cache" + "github.com/labstack/echo/v4" +) + +func registerLogsApi( + group *echo.Group, + logService *log.Service, + tokenService *token.Service, + cache *cache.Cache, +) { + group.POST("/logs", func(c echo.Context) error { + ctx := c.Request().Context() + dataset := c.Request().Header.Get("X-Fivemanage-Dataset") + + orgID, err := auth.CurrentOrgId(c) + if err != nil { + return err + } + + var logs []api.Log + if err := c.Bind(&logs); err != nil { + return err + } + + logService.SubmitLogs(ctx, orgID, dataset, logs) + + return c.JSON(200, echo.Map{ + "success": true, + }) + }, middleware.TokenAuth(tokenService, cache)) +} diff --git a/internal/http/publicapi/media_group.go b/internal/http/publicapi/media_group.go index 639a800..6a453b3 100644 --- a/internal/http/publicapi/media_group.go +++ b/internal/http/publicapi/media_group.go @@ -3,67 +3,55 @@ package publicapi import ( "net/http" + "github.com/fivemanage/lite/internal/auth" "github.com/fivemanage/lite/internal/http/httputil" + "github.com/fivemanage/lite/internal/http/middleware" "github.com/fivemanage/lite/internal/service/file" + "github.com/fivemanage/lite/internal/service/token" + "github.com/fivemanage/lite/pkg/cache" "github.com/labstack/echo/v4" ) // DRY they said -func registerMediaApi(group *echo.Group, fileService *file.Service) { +func registerMediaApi(group *echo.Group, fileService *file.Service, tokenService *token.Service, cache *cache.Cache) { group.POST("/image", func(c echo.Context) error { - var err error - ctx := c.Request().Context() - - file, header, err := httputil.File(c.Request(), "image") - if err != nil { - return c.JSON(http.StatusInternalServerError, echo.Map{ - "error": err.Error(), - }) - } - - err = fileService.CreateFile(ctx, "image", file, header) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - return c.JSON(200, nil) - }) + return handler(c, "image", fileService) + }, middleware.TokenAuth(tokenService, cache), middleware.ValidateMime("image", middleware.WhitelistedImageMIME)) group.POST("/video", func(c echo.Context) error { - var err error - ctx := c.Request().Context() - - file, header, err := httputil.File(c.Request(), "image") - if err != nil { - return c.JSON(http.StatusInternalServerError, echo.Map{ - "error": err.Error(), - }) - } - - err = fileService.CreateFile(ctx, "video", file, header) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - return c.JSON(200, nil) - }) + return handler(c, "video", fileService) + }, middleware.TokenAuth(tokenService, cache), middleware.ValidateMime("video", middleware.WhitelistedVideoMIME)) group.POST("/audio", func(c echo.Context) error { - var err error - ctx := c.Request().Context() + return handler(c, "audio", fileService) + }, middleware.TokenAuth(tokenService, cache), middleware.ValidateMime("audio", middleware.WhitelistedAudioMIME)) - file, header, err := httputil.File(c.Request(), "image") - if err != nil { - return c.JSON(http.StatusInternalServerError, echo.Map{ - "error": err.Error(), - }) - } - - err = fileService.CreateFile(ctx, "audio", file, header) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err) - } + group.POST("/file", func(c echo.Context) error { + return handler(c, "file", fileService) + }, middleware.TokenAuth(tokenService, cache), middleware.ValidateMime("file", nil)) +} - return c.JSON(200, nil) - }) +func handler(c echo.Context, fileType string, fileService *file.Service) error { + var err error + ctx := c.Request().Context() + + orgId, err := auth.CurrentOrgId(c) + if err != nil { + // we should probably have a better error here + return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized: "+err.Error()) + } + + file, header, err := httputil.File(c.Request(), fileType) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{ + "error": err.Error(), + }) + } + + err = fileService.CreateFile(ctx, orgId, file, header) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + return c.JSON(200, nil) } diff --git a/internal/http/publicapi/public_api.go b/internal/http/publicapi/public_api.go index 079b5fe..a97132e 100644 --- a/internal/http/publicapi/public_api.go +++ b/internal/http/publicapi/public_api.go @@ -2,9 +2,18 @@ package publicapi import ( "github.com/fivemanage/lite/internal/service/file" + "github.com/fivemanage/lite/internal/service/log" + "github.com/fivemanage/lite/internal/service/token" + "github.com/fivemanage/lite/pkg/cache" "github.com/labstack/echo/v4" ) -func Add(group *echo.Group, fileService *file.Service) { - registerMediaApi(group, fileService) +func Add(group *echo.Group, + fileService *file.Service, + tokenService *token.Service, + logService *log.Service, + cache *cache.Cache, +) { + registerMediaApi(group, fileService, tokenService, cache) + registerLogsApi(group, logService, tokenService, cache) } diff --git a/internal/http/publicapi/trace_group.go b/internal/http/publicapi/trace_group.go new file mode 100644 index 0000000..4f6a766 --- /dev/null +++ b/internal/http/publicapi/trace_group.go @@ -0,0 +1,11 @@ +package publicapi + +import "github.com/labstack/echo/v4" + +func registerTraceApi(group *echo.Group) { + group.GET("/traces", func(c echo.Context) error { + return c.JSON(200, echo.Map{ + "success": true, + }) + }) +} diff --git a/internal/http/server.go b/internal/http/server.go index 4cac116..c8be5ed 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -2,26 +2,30 @@ package http import ( "embed" - nethttp "net/http" + "fmt" + "io/fs" + "log/slog" + "net/http" "strings" "github.com/fivemanage/lite/internal/http/internalapi" "github.com/fivemanage/lite/internal/http/publicapi" _validator "github.com/fivemanage/lite/internal/http/validator" "github.com/fivemanage/lite/internal/service/auth" + "github.com/fivemanage/lite/internal/service/dataset" "github.com/fivemanage/lite/internal/service/file" + "github.com/fivemanage/lite/internal/service/log" + "github.com/fivemanage/lite/internal/service/organization" "github.com/fivemanage/lite/internal/service/token" + "github.com/fivemanage/lite/pkg/cache" "github.com/labstack/echo/v4" + "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho" "github.com/go-playground/validator/v10" "github.com/labstack/echo/v4/middleware" ) -// This embed shit shouldn't be this far away. -// When we are ready, we can either build the React code directly into the server or root folder -// or just copy it in the Dockerfile. - -// go:embed ../../../web/dist +//go:embed dist/* var webContent embed.FS type Server struct { @@ -30,19 +34,26 @@ type Server struct { // TODO: Add sentry for monitoring. There should be an opt-out option. func NewServer( - authservice *auth.Auth, - tokenservice *token.Service, - fileservice *file.Service, + authService *auth.Auth, + tokenService *token.Service, + fileService *file.Service, + organizationService *organization.Service, + logService *log.Service, + datasetService *dataset.Service, + memcache *cache.Cache, ) *echo.Echo { app := echo.New() + app.Debug = true - // not good, not bad - app.Validator = &_validator.CustomValidator{Validator: validator.New()} + app.Use(otelecho.Middleware("lite-api")) app.Use(middleware.Recover()) + app.Use(middleware.CORS()) + app.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20))) + + app.Validator = &_validator.CustomValidator{Validator: validator.New()} app.Use(middleware.StaticWithConfig(middleware.StaticConfig{ - Root: "web/dist", - Filesystem: nethttp.FS(webContent), + Filesystem: getFileSystem("dist"), HTML5: true, Skipper: func(c echo.Context) bool { return strings.HasPrefix(c.Request().URL.Path, "/api") @@ -50,15 +61,28 @@ func NewServer( })) apiGroup := app.Group("/api") + dashApi := apiGroup.Group("/dash") internalapi.Add( - apiGroup, - authservice, - tokenservice, + dashApi, + authService, + tokenService, + organizationService, + fileService, + datasetService, ) - publicapi.Add(apiGroup, fileservice) - - // app.imageRouterGroup(apiGroup) + publicapi.Add(apiGroup, fileService, tokenService, logService, memcache) return app } + +func getFileSystem(path string) http.FileSystem { + fs, err := fs.Sub(webContent, path) + if err != nil { + panic(err) + } + + slog.Info(fmt.Sprintf("serving static files from %s", path)) + + return http.FS(fs) +} diff --git a/internal/project/root.go b/internal/project/root.go new file mode 100644 index 0000000..a0e919a --- /dev/null +++ b/internal/project/root.go @@ -0,0 +1,16 @@ +package project + +import ( + "path/filepath" + "runtime" +) + +var ( + _, b, _, _ = runtime.Caller(0) + + Root = filepath.Join(filepath.Dir(b), "..", "..") +) + +func GetRoot() string { + return Root +} diff --git a/internal/service/admin/service.go b/internal/service/admin/service.go new file mode 100644 index 0000000..81bc696 --- /dev/null +++ b/internal/service/admin/service.go @@ -0,0 +1,32 @@ +package admin + +import ( + "context" + + "github.com/uptrace/bun" + "go.opentelemetry.io/otel" +) + +var tracer = otel.Tracer("github.com/fivemanage/lite/internal/service/admin") + +type Service struct { + db *bun.DB +} + +func NewService(db *bun.DB) *Service { + return &Service{ + db: db, + } +} + +func (r *Service) CreateMember(ctx context.Context) { +} + +func (r *Service) UpdateMember(ctx context.Context) { +} + +func (r *Service) DeleteMember(ctx context.Context) { +} + +func (r *Service) ResetMemberPassword(ctx context.Context) { +} diff --git a/internal/service/auth/auth_service.go b/internal/service/auth/auth_service.go index 7cd4990..d836575 100644 --- a/internal/service/auth/auth_service.go +++ b/internal/service/auth/auth_service.go @@ -28,7 +28,7 @@ type Auth struct { db *bun.DB } -func New(db *bun.DB) *Auth { +func NewService(db *bun.DB) *Auth { githubConfig := auth.NewGithubConfig() return &Auth{ @@ -63,7 +63,7 @@ func (a *Auth) CreateAdminUser() error { adminPassword := os.Getenv("ADMIN_PASSWORD") if adminPassword == "" { - return errors.New("Admin password is missing. Make sure ADMIN_PASSWORD is set.") + return errors.New("admin password is missing. make sure ADMIN_PASSWORD is set") } passwordHash, err := crypt.HashPassword(adminPassword) @@ -259,14 +259,48 @@ func (a *Auth) UserBySession(ctx context.Context, sessionID string) (*api.User, }, nil } +func (a *Auth) IsOrganizationMember(ctx context.Context, userID int64, organizationID string) (bool, error) { + member := new(database.OrganizationMember) + err := a.db.NewSelect().Model(member). + Where("user_id = ? AND organization_id = ?", userID, organizationID). + Limit(1). + Scan(ctx) + + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return false, err + } + + return true, nil +} + +func (a *Auth) LogoutUser(ctx context.Context, sessionID string) error { + _, err := a.db.NewDelete().Model((*database.Session)(nil)).Where("id = ?", sessionID).Exec(ctx) + return err +} + func (a *Auth) CreateSessionCookie(sessionID string) *http.Cookie { + isProduction := os.Getenv("ENV") == "production" return &http.Cookie{ Name: "fmlite_session", Value: sessionID, - Secure: false, + Secure: isProduction, SameSite: http.SameSiteLaxMode, HttpOnly: true, Path: "/", - MaxAge: 3600, + Expires: time.Now().Add(time.Hour * 24), + } +} + +func (a *Auth) CreateLogoutCookie() *http.Cookie { + return &http.Cookie{ + Name: "fmlite_session", + Value: "", + HttpOnly: true, + Path: "/", + Expires: time.Now().Add(-time.Hour), + MaxAge: -1, } } diff --git a/internal/service/auth/error.go b/internal/service/auth/error.go index 41c6b36..58b5b39 100644 --- a/internal/service/auth/error.go +++ b/internal/service/auth/error.go @@ -18,6 +18,6 @@ func (ErrUserCredentials) Error() string { } func (ErrUserCredentials) Is(target error) bool { - _, ok := target.(ErrSessionExpired) + _, ok := target.(ErrUserCredentials) return ok } diff --git a/internal/service/dataset/service.go b/internal/service/dataset/service.go new file mode 100644 index 0000000..5a28208 --- /dev/null +++ b/internal/service/dataset/service.go @@ -0,0 +1,155 @@ +package dataset + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/clickhouse" + "github.com/fivemanage/lite/internal/crypt" + "github.com/fivemanage/lite/internal/database" + datasetquery "github.com/fivemanage/lite/internal/database/query/dataset" + "github.com/uptrace/bun" +) + +type Service struct { + db *bun.DB + ch *clickhouse.Client +} + +func NewService(db *bun.DB, clickhouseClient *clickhouse.Client) *Service { + return &Service{ + db: db, + ch: clickhouseClient, + } +} + +func (r *Service) Create(ctx context.Context, orgID string, data api.Dataset) (*api.Dataset, error) { + var err error + + datasetID, err := crypt.GeneratePrimaryKey() + if err != nil { + return nil, err + } + + dbDataset := &database.Dataset{ + ID: datasetID, + Name: data.Name, + Description: data.Description, + RetentionDays: data.RetentionDays, + OrganizationID: orgID, + } + + err = datasetquery.Insert(ctx, r.db, dbDataset) + if err != nil { + return nil, err + } + + data.ID = dbDataset.ID + data.OrganizationID = orgID + + return &data, nil +} + +func (r *Service) List(ctx context.Context, organizationID string) ([]api.Dataset, error) { + var datasets []api.Dataset + + dbDatasets, err := datasetquery.List(ctx, r.db, organizationID) + if err != nil { + return nil, err + } + + for _, dbDataset := range dbDatasets { + dataset := api.Dataset{ + ID: dbDataset.ID, + Name: dbDataset.Name, + Description: dbDataset.Description, + RetentionDays: dbDataset.RetentionDays, + OrganizationID: dbDataset.OrganizationID, + } + + datasets = append(datasets, dataset) + } + return datasets, nil +} + +func (r *Service) FindByName(ctx context.Context, organizationID, datasetName string) (*database.Dataset, error) { + var err error + + if len(datasetName) == 0 { + datasetName = "default" + } + + dbDataset, err := datasetquery.SelectByName(ctx, r.db, organizationID, datasetName) + if err != nil { + return nil, err + } + + return &database.Dataset{ + ID: dbDataset.ID, + Name: dbDataset.Name, + Description: dbDataset.Description, + RetentionDays: dbDataset.RetentionDays, + OrganizationID: dbDataset.OrganizationID, + }, nil +} + +func (r *Service) ListFields(ctx context.Context, organizationID, datasetID string) ([]api.DatasetField, error) { + logKeys, err := r.ch.QueryLogFields(ctx, organizationID, datasetID) + if err != nil { + return nil, err + } + + fields := make([]api.DatasetField, len(logKeys)) + for i, key := range logKeys { + fields[i] = api.DatasetField{ + Field: key.Field, + Type: key.Type, + } + } + + return fields, nil +} + +func (r *Service) TotalLogs(ctx context.Context, organizationID, datasetID string) (int, error) { + return r.ch.QueryTotalLogs(ctx, organizationID, datasetID) +} + +func (r *Service) QueryLogs( + ctx context.Context, + organizationID, + datasetID string, + startTime, + endTime string, + filter string, + cursor int, +) ([]api.DatasetLog, error) { + filterObj := api.DatasetFilter{} + + if len(filter) != 0 { + if err := json.Unmarshal([]byte(filter), &filterObj); err != nil { + fmt.Println(err) + return nil, err + } + } + + toDateTime, err := time.Parse(time.RFC1123, endTime) + if err != nil { + return nil, err + } + fromDateTime, err := time.Parse(time.RFC1123, startTime) + if err != nil { + return nil, err + } + + fmt.Printf("toDateTime: %v, fromDateTime: %v\n", toDateTime, fromDateTime) + + logs, err := r.ch.QueryLogs(ctx, organizationID, datasetID, fromDateTime, toDateTime, filterObj, cursor) + if err != nil { + return nil, err + } + + return logs, nil +} diff --git a/internal/service/file/error.go b/internal/service/file/error.go new file mode 100644 index 0000000..ccec952 --- /dev/null +++ b/internal/service/file/error.go @@ -0,0 +1,29 @@ +package file + +import "fmt" + +type ListStorageError struct { + ErrorMsg string +} + +func (e ListStorageError) Error() string { + return fmt.Sprintf("failed to list storage files: %s", e.ErrorMsg) +} + +func (ListStorageError) Is(target error) bool { + _, ok := target.(ListStorageError) + return ok +} + +type UploadStorageError struct { + ErrorMsg string +} + +func (e UploadStorageError) Error() string { + return fmt.Sprintf("failed to upload storage file: %s", e.ErrorMsg) +} + +func (UploadStorageError) Is(target error) bool { + _, ok := target.(UploadStorageError) + return ok +} diff --git a/internal/service/file/file_service.go b/internal/service/file/file_service.go index 994150c..c71b50a 100644 --- a/internal/service/file/file_service.go +++ b/internal/service/file/file_service.go @@ -1,15 +1,19 @@ package file import ( + "bytes" "context" - "fmt" + "errors" + "io" "mime/multipart" + "github.com/fivemanage/lite/api" "github.com/fivemanage/lite/internal/crypt" "github.com/fivemanage/lite/internal/database" - filequery "github.com/fivemanage/lite/internal/database/query/file" - "github.com/fivemanage/lite/internal/storage" - "github.com/gabriel-vasile/mimetype" + "github.com/fivemanage/lite/internal/database/query/file" + "github.com/fivemanage/lite/internal/http/httputil" + "github.com/fivemanage/lite/pkg/storage" + "github.com/sirupsen/logrus" "github.com/uptrace/bun" ) @@ -27,53 +31,212 @@ func NewService(db *bun.DB, storageLayer storage.StorageLayer) *Service { func (s *Service) CreateFile( ctx context.Context, - fileType string, + organizationID string, file multipart.File, fileHeader *multipart.FileHeader, ) error { var err error - key, contentType, err := generateFileKey(fileType, file, fileHeader) + var key string + + primaryKey, err := crypt.GeneratePrimaryKey() + if err != nil { + return UploadStorageError{ + ErrorMsg: err.Error(), + } + } + + mimeType, ext, fileType, err := httputil.GetMimeDetails(fileHeader, file) + if err != nil { + return UploadStorageError{ + ErrorMsg: errors.New("failed to get mime type").Error(), + } + } + + key, err = generateFileKey(organizationID, ext) + if err != nil { + return UploadStorageError{ + ErrorMsg: errors.New("failed to generate file key").Error(), + } + } + + tx, err := filequery.Create(ctx, s.db, &database.Asset{ + ID: primaryKey, + Type: fileType, + Size: fileHeader.Size, + OrganizationID: organizationID, + Key: key, + }) + if err != nil { + return err + } + + // you'd think we didn't need to do this, but the since we read the file before this step to get mime type and shit + // we need to reset the file pointer and read it again + buffer, err := s.encode(file, fileHeader) + if err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") + if err := tx.Rollback(); err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") + return err + } + + return UploadStorageError{ + ErrorMsg: err.Error(), + } + } + + err = s.storage.UploadFile(ctx, buffer, key, mimeType) + if err != nil { + if err := tx.Rollback(); err != nil { + return err + } + + return err + } + + // this is a bit tricky, but if this fails....then...oh well + err = tx.Commit() if err != nil { return err } - dbFile := &database.File{ - Type: fileType, - Size: fileHeader.Size, - Key: key, + return nil +} + +// this is basically the function that is used for any dashboard uploads +func (s *Service) CreateStorageFile( + ctx context.Context, + organizationID string, + file multipart.File, + fileHeader *multipart.FileHeader, +) error { + var err error + + primaryKey, err := crypt.GeneratePrimaryKey() + if err != nil { + return UploadStorageError{ + ErrorMsg: err.Error(), + } + } + + mimeType, _, fileType, err := httputil.GetMimeDetails(fileHeader, file) + if err != nil { + return UploadStorageError{ + ErrorMsg: errors.New("failed to get mime type").Error(), + } } - tx, err := filequery.Create(ctx, s.db, dbFile) + // fileHeader.Filename has the extension most of the time + // should it become an issue, we can look for it and check if its empty; + key := generateWebKey(organizationID, fileHeader.Filename) + + tx, err := filequery.Create(ctx, s.db, &database.Asset{ + ID: primaryKey, + OrganizationID: organizationID, + Type: fileType, + Size: fileHeader.Size, + Key: key, + }) if err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") return err } - err = s.storage.UploadFile(ctx, file, key, contentType) + buffer, err := s.encode(file, fileHeader) + if err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") + if err := tx.Rollback(); err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") + return err + } + + return UploadStorageError{ + ErrorMsg: err.Error(), + } + } + + err = s.storage.UploadFile(ctx, buffer, key, mimeType) if err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") + if err := tx.Rollback(); err != nil { + logrus.WithError(err).WithField("organization_id", organizationID).Error("FileService.CreateStorageFile") return err } + return UploadStorageError{ + ErrorMsg: err.Error(), + } + } + + err = tx.Commit() + if err != nil { return err } return nil } -func generateFileKey(fileType string, file multipart.File, fileHeader *multipart.FileHeader) (string, string, error) { - filename, err := crypt.GenerateFilename() +func (s *Service) ListStorageFiles( + ctx context.Context, + organizationID string, + search string, +) (*api.AssetResponse, error) { + var err error + + files, err := filequery.FindStorageFiles(ctx, s.db, organizationID, search) if err != nil { - return "", "", err + storageError := &ListStorageError{ + ErrorMsg: err.Error(), + } + logrus.WithError(storageError). + WithField("organization_id", organizationID). + Error("FileService.ListStorageFiles") + return nil, storageError } - buf := make([]byte, fileHeader.Size) - _, err = file.Read(buf) + assets := make([]*api.Asset, len(files)) + for i, file := range files { + assets[i] = &api.Asset{ + ID: file.ID, + Type: file.Type, + Key: file.Key, + Size: file.Size, + CreatedAt: file.CreatedAt, + } + } + + totalCount, err := filequery.FindTotalStorageCount(ctx, s.db, organizationID) if err != nil { - return "", "", err + storageError := &ListStorageError{ + ErrorMsg: err.Error(), + } + + logrus.WithError(storageError). + WithField("organization_id", organizationID).Error("FileService.ListStorageFiles") + + return nil, storageError + } + + response := &api.AssetResponse{ + StorageFiles: assets, + TotalCount: totalCount, + } + + return response, nil +} + +func (s *Service) encode(file multipart.File, header *multipart.FileHeader) (*bytes.Reader, error) { + if _, err := file.Seek(0, io.SeekStart); err != nil { + return nil, err } - mime := mimetype.Detect(buf) - key := fmt.Sprintf("%s/%s.%s", fileType, filename, mime.Extension()) + buf := make([]byte, header.Size) + _, err := file.Read(buf) + if err != nil { + return nil, err + } - return key, mime.String(), nil + reader := bytes.NewReader(buf) + return reader, nil } diff --git a/internal/service/file/key.go b/internal/service/file/key.go new file mode 100644 index 0000000..081fa89 --- /dev/null +++ b/internal/service/file/key.go @@ -0,0 +1,22 @@ +package file + +import ( + "fmt" + + "github.com/fivemanage/lite/internal/crypt" +) + +func generateFileKey(organizationID string, ext string) (string, error) { + filename, err := crypt.GenerateFilename() + if err != nil { + return "", err + } + + key := fmt.Sprintf("%s/%s%s", organizationID, filename, ext) + return key, nil +} + +func generateWebKey(organizationID, filename string) string { + key := fmt.Sprintf("%s/%s", organizationID, filename) + return key +} diff --git a/internal/service/log/attribute.go b/internal/service/log/attribute.go new file mode 100644 index 0000000..b3da13d --- /dev/null +++ b/internal/service/log/attribute.go @@ -0,0 +1,69 @@ +package log + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +const MaxLogAttributesDepth uint8 = 255 + +func buildLogAttributes(k string, v interface{}, depth uint8) map[string]string { + if depth >= MaxLogAttributesDepth { + return nil + } + + if vStr, ok := v.(string); ok { + trimmedStr := strings.TrimSpace(vStr) + re, err := regexp.Compile(`\s+`) + if err != nil { + return map[string]string{k: trimmedStr} + } + + singleSpaceStr := re.ReplaceAllString(trimmedStr, " ") + return map[string]string{k: singleSpaceStr} + } + + if vInt, ok := v.(int64); ok { + return map[string]string{k: strconv.FormatInt(vInt, 10)} + } + if vFlt, ok := v.(float64); ok { + return map[string]string{k: strconv.FormatFloat(vFlt, 'f', -1, 64)} + } + + if vSlice, ok := v.([]interface{}); ok && len(vSlice) > 0 { + m := make(map[string]string) + for idx, sliceV := range vSlice { + sliceKey := fmt.Sprintf("%s.%d", k, idx) + for k2, v2 := range buildLogAttributes(sliceKey, sliceV, depth+1) { + m[k2] = v2 + } + } + return m + } + + if vMap, ok := v.(map[string]interface{}); ok { + m := make(map[string]string) + for mapKey, mapV := range vMap { + for k2, v2 := range buildLogAttributes(mapKey, mapV, depth+1) { + m[fmt.Sprintf("%s.%s", k, k2)] = v2 + } + } + + return m + } + + return nil +} + +func formatMessage(message string) string { + trimmedStr := strings.TrimSpace(message) + re, err := regexp.Compile(`\s+`) + if err != nil { + return trimmedStr + } + + singleSpaceStr := re.ReplaceAllString(trimmedStr, " ") + return singleSpaceStr +} diff --git a/internal/service/log/log_service.go b/internal/service/log/log_service.go new file mode 100644 index 0000000..8da131c --- /dev/null +++ b/internal/service/log/log_service.go @@ -0,0 +1,78 @@ +package log + +import ( + "context" + "maps" + "time" + + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/clickhouse" + "github.com/fivemanage/lite/internal/crypt" + "github.com/fivemanage/lite/internal/service/dataset" + "github.com/fivemanage/lite/pkg/kafkaqueue" + "github.com/uptrace/bun" + "github.com/uptrace/opentelemetry-go-extra/otelzap" + "go.uber.org/zap" +) + +type Service struct { + db *bun.DB + kafkaProducer *kafkaqueue.Queue + datasetService *dataset.Service +} + +func NewService(db *bun.DB, kafkaProducer *kafkaqueue.Queue, clickhouseClient *clickhouse.Client, datasetService *dataset.Service) *Service { + return &Service{ + db: db, + kafkaProducer: kafkaProducer, + datasetService: datasetService, + } +} + +func (r *Service) SubmitLogs(ctx context.Context, organizationId string, datasetName string, logs []api.Log) { + clickhouseLogs := make([]*clickhouse.Log, len(logs)) + + for i, log := range logs { + traceID, err := crypt.GeneratePrimaryKey() + if err != nil { + otelzap.L().Error("failed to generate trace id", zap.Error(err)) + break + } + + dataset, err := r.datasetService.FindByName(ctx, organizationId, datasetName) + if err != nil { + otelzap.L().Error("failed to find dataset", zap.Error(err)) + break + } + + timestamp := time.Now().UTC() + metadata := make(map[string]string) + + for k, v := range log.Metadata { + maps.Copy(metadata, buildLogAttributes(k, v, 0)) + } + + // fix log message because it can be silly sometimes + logMessage := formatMessage(log.Message) + + // these should override any metadata that matches + metadata["message"] = logMessage + metadata["severity"] = log.Level + metadata["_resource"] = log.Resource + + clickhouseLogs[i] = &clickhouse.Log{ + TraceID: traceID, + Timestamp: timestamp, + TeamID: organizationId, + DatasetID: dataset.ID, + Body: logMessage, + Attributes: metadata, + RetentionDays: 30, + } + } + + err := r.kafkaProducer.Submit(ctx, clickhouseLogs) + if err != nil { + otelzap.L().Error("failed to submit logs to kafka", zap.Error(err)) + } +} diff --git a/internal/service/organization/organization_service.go b/internal/service/organization/organization_service.go new file mode 100644 index 0000000..0143453 --- /dev/null +++ b/internal/service/organization/organization_service.go @@ -0,0 +1,97 @@ +package organization + +import ( + "context" + + "github.com/fivemanage/lite/api" + "github.com/fivemanage/lite/internal/crypt" + "github.com/fivemanage/lite/internal/database" + organizationquery "github.com/fivemanage/lite/internal/database/query/organization" + "github.com/uptrace/bun" +) + +type Service struct { + db *bun.DB +} + +func NewService(db *bun.DB) *Service { + return &Service{ + db: db, + } +} + +func (r *Service) CreateOrganization(ctx context.Context, data *api.CreateOrganizationRequest, userID int64) (*api.Organization, error) { + orgId, err := crypt.GeneratePrimaryKey() + if err != nil { + return nil, err + } + + dbOrganization := &database.Organization{ + Name: data.Name, + ID: orgId, + } + + orgTx, err := organizationquery.Create(ctx, r.db, dbOrganization) + if err != nil { + return nil, err + } + if err := orgTx.Commit(); err != nil { + return nil, err + } + + memberTx, err := organizationquery.CreateMember(ctx, r.db, &database.OrganizationMember{ + Role: "ADMIN", + OrganizationID: dbOrganization.ID, + UserID: userID, + }) + if err != nil { + return nil, err + } + + if err := memberTx.Commit(); err != nil { + if err := orgTx.Rollback(); err != nil { + return nil, err + } + return nil, err + } + + organization := &api.Organization{ + ID: dbOrganization.ID, + Name: dbOrganization.Name, + } + + return organization, nil +} + +func (r *Service) FindOrganizationByID(ctx context.Context, ID string) (*api.Organization, error) { + dbOrganization, err := organizationquery.Find(ctx, r.db, ID) + if err != nil { + return nil, err + } + + organization := &api.Organization{ + ID: dbOrganization.ID, + Name: dbOrganization.Name, + } + + return organization, nil +} + +func (r *Service) ListOrganizations(ctx context.Context) ([]*api.Organization, error) { + dbOrganizations, err := organizationquery.List(ctx, r.db) + if err != nil { + return nil, err + } + + organizations := make([]*api.Organization, 0, len(dbOrganizations)) + for _, dbOrganization := range dbOrganizations { + organization := &api.Organization{ + ID: dbOrganization.ID, + Name: dbOrganization.Name, + } + + organizations = append(organizations, organization) + } + + return organizations, nil +} diff --git a/internal/service/token/token_service.go b/internal/service/token/token_service.go index 78dfc34..f19321c 100644 --- a/internal/service/token/token_service.go +++ b/internal/service/token/token_service.go @@ -2,6 +2,7 @@ package token import ( "context" + "os" "strconv" "github.com/fivemanage/lite/api" @@ -9,8 +10,14 @@ import ( "github.com/fivemanage/lite/internal/database" tokenquery "github.com/fivemanage/lite/internal/database/query/token" "github.com/uptrace/bun" + "github.com/uptrace/opentelemetry-go-extra/otelzap" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.uber.org/zap" ) +var tracer = otel.Tracer("github.com/fivemanage/lite/internal/service/token") + type Service struct { db *bun.DB } @@ -21,22 +28,32 @@ func NewService(db *bun.DB) *Service { } } -func (r *Service) CreateToken(ctx context.Context, data *api.CreateTokenRequest) (string, error) { - var err error +func (r *Service) GetToken(ctx context.Context, apiToken string) (*database.Token, error) { + // we need to add this check to the env check in cmd/lite/lite.go + hmacSecret := os.Getenv("API_TOKEN_HMAC_SECRET") + tokenHash := crypt.ComputeHMAC(hmacSecret, apiToken) - apiToken, err := crypt.GenerateApiKey() + token, err := tokenquery.SelectByHash(ctx, r.db, tokenHash) if err != nil { - return "", err + return nil, err } - tokenHash, err := crypt.HashPassword(apiToken) + return token, nil +} + +func (r *Service) CreateToken(ctx context.Context, data *api.CreateTokenRequest) (string, error) { + apiToken, err := crypt.GenerateApiKey() if err != nil { return "", err } + hmacSecret := os.Getenv("API_TOKEN_HMAC_SECRET") + tokenHash := crypt.ComputeHMAC(hmacSecret, apiToken) + token := &database.Token{ - Identifier: data.Identifier, - TokenHash: tokenHash, + OrganizationID: data.OrganizationID, + Identifier: data.Identifier, + TokenHash: tokenHash, } err = tokenquery.Create(ctx, r.db, token) @@ -47,22 +64,33 @@ func (r *Service) CreateToken(ctx context.Context, data *api.CreateTokenRequest) return apiToken, nil } -func (r *Service) ListTokens(ctx context.Context) ([]api.ListTokensResponse, error) { +func (r *Service) ListTokens(ctx context.Context) ([]*api.ListTokensResponse, error) { var err error - var response []api.ListTokensResponse + var response []*api.ListTokensResponse + + ctx, span := tracer.Start(ctx, "token.list_tokens") + defer span.End() tokens, err := tokenquery.List(ctx, r.db) if err != nil { + span.RecordError(err) + otelzap.L().Error("failed to list tokens", zap.Error(err)) return nil, err } for _, token := range tokens { - response = append(response, api.ListTokensResponse{ + response = append(response, &api.ListTokensResponse{ ID: token.ID, Identifier: token.Identifier, }) } + span.SetAttributes( + attribute.Int("token_count", len(tokens)), + ) + + otelzap.L().Debug("listed tokens successfully", zap.Int("count", len(tokens))) + return response, nil } diff --git a/internal/storage/s3/s3.go b/internal/storage/s3/s3.go deleted file mode 100644 index f7c4f90..0000000 --- a/internal/storage/s3/s3.go +++ /dev/null @@ -1,56 +0,0 @@ -package s3 - -import ( - "context" - "io" - "os" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/s3" -) - -type Storage struct { - client *s3.Client -} - -func New() *Storage { - cfg, err := config.LoadDefaultConfig(context.TODO(), - config.WithRegion("auto"), - ) - if err != nil { - // TODO: handle error properly - panic(err) - } - - // s3 will automatically use the following environment variables: - // AWS_ACCESS_KEY_ID - // AWS_SECRET_ACCESS_KEY - // these are custom: - // AWS_ENDPOINT - // AWS_BUCKET - client := s3.NewFromConfig(cfg, func(o *s3.Options) { - o.BaseEndpoint = aws.String(os.Getenv("AWS_ENDPOINT")) - }) - - return &Storage{ - client: client, - } -} - -// UploadFile will both upload and replace as long as the key is the same -func (s *Storage) UploadFile(ctx context.Context, file io.Reader, key, contenType string) error { - s.client.PutObject(ctx, &s3.PutObjectInput{ - Bucket: aws.String(os.Getenv("AWS_BUCKET")), - Key: aws.String(key), - Body: file, - ContentType: aws.String(contenType), - }) - - return nil -} - -func (s *Storage) DeleteFile() error { - s.client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{}) - return nil -} diff --git a/migrate/migrate.go b/migrate/migrate.go index c8575b8..77669ac 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -7,10 +7,12 @@ import ( "github.com/fivemanage/lite/internal/database" "github.com/fivemanage/lite/migrate/migrations" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/uptrace/bun" "github.com/uptrace/bun/migrate" + "github.com/uptrace/opentelemetry-go-extra/otelzap" ) var ( @@ -24,7 +26,8 @@ var ( Short: "Create migration tables", Run: func(cmd *cobra.Command, args []string) { driver := viper.GetString("driver") - db := database.New(driver, "").Connect() + dsn := viper.GetString("dsn") + db := database.New(driver).Connect(dsn) migrator := migrate.NewMigrator(db, migrations.Migrations) err := migrator.Init(cmd.Context()) @@ -39,7 +42,9 @@ var ( Short: "Migrate database", Run: func(cmd *cobra.Command, args []string) { driver := viper.GetString("driver") - db := database.New(driver, "").Connect() + dsn := viper.GetString("dsn") + + db := database.New(driver).Connect(dsn) migrator := migrate.NewMigrator(db, migrations.Migrations) if err := migrator.Lock(cmd.Context()); err != nil { @@ -52,9 +57,10 @@ var ( panic(err) } if group.IsZero() { - fmt.Printf("there are no new migrations to run (database is up to date)\n") + otelzap.S().Info("there are no new migrations to run (database is up to date)") } - fmt.Printf("migrated to %s\n", group) + + otelzap.S().Infof("migrated to %s", group) }, } @@ -62,7 +68,8 @@ var ( Use: "unlock", Run: func(cmd *cobra.Command, args []string) { driver := viper.GetString("driver") - db := database.New(driver, "").Connect() + dsn := viper.GetString("dsn") + db := database.New(driver).Connect(dsn) migrator := migrate.NewMigrator(db, migrations.Migrations) err := migrator.Unlock(cmd.Context()) @@ -76,7 +83,8 @@ var ( Use: "lock", Run: func(cmd *cobra.Command, args []string) { driver := viper.GetString("driver") - db := database.New(driver, "").Connect() + dsn := viper.GetString("dsn") + db := database.New(driver).Connect(dsn) migrator := migrate.NewMigrator(db, migrations.Migrations) err := migrator.Lock(cmd.Context()) @@ -91,20 +99,31 @@ var ( Short: "Create database migration", Run: func(cmd *cobra.Command, args []string) { driver := viper.GetString("driver") - db := database.New(driver, "").Connect() + dsn := viper.GetString("dsn") + db := database.New(driver).Connect(dsn) migrator := migrate.NewMigrator(db, migrations.Migrations) name := strings.Join(args, "_") - mf, err := migrator.CreateGoMigration(cmd.Context(), name) + mf, err := migrator.CreateSQLMigrations(cmd.Context(), name) if err != nil { panic(err) } - fmt.Printf("created migration %s (%s)\n", mf.Name, mf.Path) + + for _, m := range mf { + fmt.Printf("created migration %s (%s)\n", m.Name, m.Path) + } }, } ) +func InitMigration(ctx context.Context, db *bun.DB) { + migrator := migrate.NewMigrator(db, migrations.Migrations) + if err := migrator.Init(ctx); err != nil { + panic(err) + } +} + func AutoMigrate(ctx context.Context, db *bun.DB) { migrator := migrate.NewMigrator(db, migrations.Migrations) if err := migrator.Lock(ctx); err != nil { @@ -117,7 +136,9 @@ func AutoMigrate(ctx context.Context, db *bun.DB) { panic(err) } if group.IsZero() { - fmt.Printf("there are no new migrations to run (database is up to date)\n") + otelzap.S().Info("there are no new migrations to run (database is up to date)") + return } - fmt.Printf("migrated to %s\n", group) + + logrus.Infof("migrated to %s", group) } diff --git a/migrate/migrations/20250207213625_add_username_column.go b/migrate/migrations/20250331204401_create_organization_table.go similarity index 66% rename from migrate/migrations/20250207213625_add_username_column.go rename to migrate/migrations/20250331204401_create_organization_table.go index 4a74f74..f913165 100644 --- a/migrate/migrations/20250207213625_add_username_column.go +++ b/migrate/migrations/20250331204401_create_organization_table.go @@ -4,16 +4,19 @@ import ( "context" "fmt" + "github.com/fivemanage/lite/internal/database" "github.com/uptrace/bun" ) func init() { Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { fmt.Print(" [up migration] ") - _, err := db.NewAddColumn().Table("user").ColumnExpr("username TEXT NOT NULL").Exec(ctx) + db.RegisterModel((*database.Organization)(nil)) + _, err := db.NewCreateTable().Model((*database.Organization)(nil)).Exec(ctx) if err != nil { return err } + return nil }, func(ctx context.Context, db *bun.DB) error { fmt.Print(" [down migration] ") diff --git a/migrate/migrations/20250207222546_add_is_admin.go b/migrate/migrations/20250406011009_add_organization_member_table.go similarity index 64% rename from migrate/migrations/20250207222546_add_is_admin.go rename to migrate/migrations/20250406011009_add_organization_member_table.go index d1f9f90..2d6b698 100644 --- a/migrate/migrations/20250207222546_add_is_admin.go +++ b/migrate/migrations/20250406011009_add_organization_member_table.go @@ -4,16 +4,15 @@ import ( "context" "fmt" + "github.com/fivemanage/lite/internal/database" "github.com/uptrace/bun" ) func init() { Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { fmt.Print(" [up migration] ") - _, err := db.NewAddColumn(). - Table("user"). - ColumnExpr("is_admin BOOL NOT NULL DEFAULT false"). - Exec(ctx) + db.RegisterModel((*database.OrganizationMember)(nil)) + _, err := db.NewCreateTable().Model((*database.OrganizationMember)(nil)).Exec(ctx) if err != nil { return err } diff --git a/migrate/migrations/20250508174146_create_assets_table.go b/migrate/migrations/20250508174146_create_assets_table.go new file mode 100644 index 0000000..acc0a65 --- /dev/null +++ b/migrate/migrations/20250508174146_create_assets_table.go @@ -0,0 +1,31 @@ +package migrations + +import ( + "context" + "fmt" + + "github.com/fivemanage/lite/internal/database" + "github.com/uptrace/bun" +) + +func init() { + Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { + fmt.Print(" [up migration] ") + db.RegisterModel((*database.Asset)(nil)) + _, err := db.NewCreateTable().Model((*database.Asset)(nil)).Exec(ctx) + if err != nil { + return err + } + + return nil + }, func(ctx context.Context, db *bun.DB) error { + fmt.Print(" [down migration] ") + + _, err := db.NewDropTable().Model((*database.Asset)(nil)).IfExists().Exec(ctx) + if err != nil { + return err + } + + return nil + }) +} diff --git a/migrate/migrations/20250705161348_create_dataset_table.down.sql b/migrate/migrations/20250705161348_create_dataset_table.down.sql new file mode 100644 index 0000000..04fb950 --- /dev/null +++ b/migrate/migrations/20250705161348_create_dataset_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS dataset; diff --git a/migrate/migrations/20250705161348_create_dataset_table.up.sql b/migrate/migrations/20250705161348_create_dataset_table.up.sql new file mode 100644 index 0000000..daac1de --- /dev/null +++ b/migrate/migrations/20250705161348_create_dataset_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS dataset ( + id VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, + description VARCHAR(255) NOT NULL, + retention_days INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + organization_id VARCHAR(255) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (organization_id) REFERENCES organization(id) +); + +CREATE INDEX IF NOT EXISTS dataset_organization_id_idx ON dataset (organization_id); +CREATE INDEX IF NOT EXISTS dataset_name_idx ON dataset (name); diff --git a/migrate/migrations/main.go b/migrate/migrations/main.go index 781c88d..29ca1a5 100644 --- a/migrate/migrations/main.go +++ b/migrate/migrations/main.go @@ -1,11 +1,19 @@ package migrations -import "github.com/uptrace/bun/migrate" +import ( + "embed" + + "github.com/uptrace/bun/migrate" +) + +//go:embed *.go +//go:embed *.sql +var sqlMigrations embed.FS var Migrations = migrate.NewMigrations() func init() { - if err := Migrations.DiscoverCaller(); err != nil { + if err := Migrations.Discover(sqlMigrations); err != nil { panic(err) } } diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go new file mode 100644 index 0000000..f29d827 --- /dev/null +++ b/pkg/cache/cache.go @@ -0,0 +1,105 @@ +package cache + +import ( + "sync" + "time" +) + +const ( + TokenCacheKey = "tokenHash_%s.data" +) + +type Item struct { + Object any + Expiration int64 +} + +type memcache struct { + items map[string]Item + defaultExpiration int64 + mutex sync.Mutex +} + +type Cache struct { + *memcache +} + +type mop struct { + interval time.Duration + stop chan bool +} + +func NewMemcache(defaultExpiration int64) *Cache { + c := &Cache{ + memcache: &memcache{ + items: make(map[string]Item), + defaultExpiration: defaultExpiration, + }, + } + + m := &mop{ + interval: time.Minute, + stop: make(chan bool), + } + + go m.start(c) + + return c +} + +func (c *Cache) Set(key string, value any, expiration time.Duration) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if expiration <= 0 { + expiration = time.Duration(c.defaultExpiration) + } + + c.items[key] = Item{ + Object: value, + Expiration: time.Now().Add(expiration).Unix(), + } +} + +func (c *Cache) Get(key string) (any, bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + + item, found := c.items[key] + if !found || (item.Expiration > 0 && item.Expiration < time.Now().Unix()) { + return nil, false + } + + return item.Object, true +} + +func (c *Cache) Delete(key string) { + c.mutex.Lock() + defer c.mutex.Unlock() + + delete(c.items, key) +} + +func (m *mop) start(c *Cache) { + m.stop = make(chan bool) + go func() { + ticker := time.NewTicker(m.interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + c.mutex.Lock() + now := time.Now().Unix() + for key, item := range c.items { + if item.Expiration > 0 && item.Expiration < now { + delete(c.items, key) + } + } + c.mutex.Unlock() + case <-m.stop: + return + } + } + }() +} diff --git a/pkg/kafkaqueue/kafkaqueue.go b/pkg/kafkaqueue/kafkaqueue.go new file mode 100644 index 0000000..21f35b5 --- /dev/null +++ b/pkg/kafkaqueue/kafkaqueue.go @@ -0,0 +1,155 @@ +package kafkaqueue + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + "time" + + "github.com/fivemanage/lite/internal/clickhouse" + "github.com/segmentio/kafka-go" + "github.com/uptrace/opentelemetry-go-extra/otelzap" + "go.uber.org/zap" +) + +// wow, so much randomness +const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +const ( + KafkaOperationTimeout = 25 * time.Second + // again, i wont set up partitions + ConsumerGroupName = "default-group" +) + +type TopicType string + +const ( + TopicTypeBatched TopicType = "batched" +) + +type Queue struct { + kafkaP *kafka.Writer + kafkaC *kafka.Reader + logger *otelzap.Logger +} + +// heh, fun +func GenerateRandomString(length int) string { + b := make([]byte, length) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func logf(msg string, a ...any) { + fmt.Printf(msg, a...) + fmt.Println() +} + +func NewProducer(logger *otelzap.Logger) *Queue { + writer := &kafka.Writer{ + Addr: kafka.TCP("localhost:9094"), + // tbh, I'm just not feeling like setting up partitions + // sooooo, topics and groups will have to do it + Topic: "batched", + Balancer: &kafka.LeastBytes{}, + Async: true, + ReadTimeout: KafkaOperationTimeout, + WriteTimeout: KafkaOperationTimeout, + AllowAutoTopicCreation: true, + ErrorLogger: kafka.LoggerFunc(logf), + } + + return &Queue{ + kafkaP: writer, + logger: logger, + } +} + +func NewConsumer(logger *otelzap.Logger) *Queue { + dialer := &kafka.Dialer{ + Timeout: KafkaOperationTimeout, + DualStack: true, + } + + reader := kafka.NewReader(kafka.ReaderConfig{ + Dialer: dialer, + Brokers: []string{"localhost:9094"}, + Topic: "batched", + GroupID: ConsumerGroupName, + MaxBytes: 10e6, + ErrorLogger: kafka.LoggerFunc(logf), + }) + + return &Queue{ + kafkaC: reader, + logger: logger, + } +} + +func (q *Queue) Submit(ctx context.Context, messages []*clickhouse.Log) error { + var kMessages []kafka.Message + for _, v := range messages { + key := GenerateRandomString(32) + + msgBytes, err := q.serializeMessage(v) + if err != nil { + // todo: Log this + return err + } + + kMessages = append(kMessages, kafka.Message{ + Key: []byte(key), + Value: msgBytes, + }) + } + + err := q.kafkaP.WriteMessages(ctx, kMessages...) + if err != nil { + fmt.Println("failed to submit log", err.Error()) + return err + } + + otelzap.L().Info("submitted log to kafka", zap.String("topic", "batched"), zap.Int("count", len(messages))) + + return nil +} + +func (q *Queue) ReadMessage(ctx context.Context) *clickhouse.Log { + m, err := q.kafkaC.ReadMessage(ctx) + if err != nil { + if err.Error() != "fetching message: context deadline exceeded" { + otelzap.L().Error("failed to read message from kafka", zap.Error(err)) + } + + return nil + } + + msg, err := q.unmarshalMessage(m.Value) + if err != nil { + return nil + } + + return msg +} + +func (q *Queue) unmarshalMessage(data []byte) (*clickhouse.Log, error) { + var log clickhouse.Log + err := json.Unmarshal(data, &log) + if err != nil { + return nil, err + } + + return &log, nil +} + +func (q *Queue) serializeMessage(v any) ([]byte, error) { + b, err := json.Marshal(v) + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/pkg/kafkaqueue/worker.go b/pkg/kafkaqueue/worker.go new file mode 100644 index 0000000..148b7ce --- /dev/null +++ b/pkg/kafkaqueue/worker.go @@ -0,0 +1,60 @@ +package kafkaqueue + +import ( + "context" + "fmt" + "time" + + "github.com/fivemanage/lite/internal/clickhouse" + "github.com/uptrace/opentelemetry-go-extra/otelzap" + "go.uber.org/zap" +) + +const ( + DefaultBatchFlushSize = 10000 + DefaultBatchedFlushTimeout = 3 * time.Second +) + +type KafkaBatchWorker struct { + consumer *Queue + logs []*clickhouse.Log + lastFlush time.Time + clickhouseClient *clickhouse.Client +} + +func NewBatchWorker(clickhouseClient *clickhouse.Client, kafkaConsumer *Queue) *KafkaBatchWorker { + return &KafkaBatchWorker{ + consumer: kafkaConsumer, + logs: []*clickhouse.Log{}, + lastFlush: time.Now(), + clickhouseClient: clickhouseClient, + } +} + +func (r *KafkaBatchWorker) ProcessMessages() { + for { + func(ctx context.Context) { + readCtx, readCancel := context.WithTimeout(ctx, DefaultBatchedFlushTimeout) + defer readCancel() + + msg := r.consumer.ReadMessage(readCtx) + if msg != nil { + r.logs = append(r.logs, msg) + } + + if time.Since(r.lastFlush) > DefaultBatchedFlushTimeout || len(r.logs) > DefaultBatchFlushSize { + err := r.clickhouseClient.BatchWriteLogRows(ctx, r.logs) + if err != nil { + fmt.Println("failed to batch write logs", err.Error()) + } + + if len(r.logs) > 0 { + otelzap.L().Info("flushed logs to clickhouse", zap.Int("count", len(r.logs))) + } + + r.lastFlush = time.Now() + r.logs = []*clickhouse.Log{} + } + }(context.Background()) + } +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..f59fddb --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,71 @@ +package logger + +import ( + "context" + "io" + "log" + "log/slog" + "os" + + "github.com/fatih/color" +) + +type PrettyHandlerOptions struct { + SlogOpts slog.HandlerOptions +} + +type PrettyHandler struct { + slog.Handler + l *log.Logger +} + +func New() { + handler := NewPrettyHandler(os.Stdout, PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + }, + }) + slog.SetDefault(slog.New(handler)) +} + +func (h *PrettyHandler) Handle(ctx context.Context, r slog.Record) error { + level := r.Level.String() + ":" + + switch r.Level { + case slog.LevelDebug: + level = color.MagentaString(level) + case slog.LevelInfo: + level = color.BlueString(level) + case slog.LevelWarn: + level = color.YellowString(level) + case slog.LevelError: + level = color.RedString(level) + } + + fields := make(map[string]interface{}, r.NumAttrs()) + r.Attrs(func(a slog.Attr) bool { + fields[a.Key] = a.Value.Any() + + return true + }) + + timeStr := r.Time.Format("[15:05:05.000]") + msg := color.CyanString(r.Message) + + h.l.Println(timeStr, level, msg) + + return nil +} + +func NewPrettyHandler( + out io.Writer, + opts PrettyHandlerOptions, +) *PrettyHandler { + h := &PrettyHandler{ + Handler: slog.NewJSONHandler(out, &opts.SlogOpts), + l: log.New(out, "", 0), + } + + return h +} diff --git a/pkg/logger/zap_logger.go b/pkg/logger/zap_logger.go new file mode 100644 index 0000000..5f1c987 --- /dev/null +++ b/pkg/logger/zap_logger.go @@ -0,0 +1,13 @@ +package logger + +import ( + "github.com/sirupsen/logrus" +) + +func NewLogger() { + textFormatter := new(logrus.TextFormatter) + textFormatter.FullTimestamp = true + textFormatter.TimestampFormat = "2006-01-02 15:04:05" + textFormatter.PadLevelText = true + logrus.SetFormatter(textFormatter) +} diff --git a/pkg/otel/exporter.go b/pkg/otel/exporter.go new file mode 100644 index 0000000..907e4f6 --- /dev/null +++ b/pkg/otel/exporter.go @@ -0,0 +1,65 @@ +package otel + +import ( + "context" + "crypto/tls" + "os" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" +) + +const ( + serviceName = "lite-api" + otlpEndpoint = "localhost:4318" +) + +func SetupTracer() (func(context.Context) error, error) { + ctx := context.Background() + return InstallExportPipeline(ctx) +} + +func Resource() *resource.Resource { + // Defines resource with service name, version, and environment. + return resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(serviceName), + semconv.ServiceVersionKey.String(os.Getenv("VERSION")), + attribute.String("environment", os.Getenv("ENV")), + ) +} + +func InstallExportPipeline(ctx context.Context) (func(context.Context) error, error) { + var tlsOption otlptracehttp.Option + if os.Getenv("ENV") == "dev" { + tlsOption = otlptracehttp.WithInsecure() + } else { + tlsOption = otlptracehttp.WithTLSClientConfig(&tls.Config{}) + } + + exporter, err := otlptracehttp.New(ctx, + otlptracehttp.WithEndpoint(otlpEndpoint), + tlsOption, + ) + if err != nil { + return nil, err + } + + tracerProvider := trace.NewTracerProvider( + trace.WithBatcher(exporter), + trace.WithResource(Resource()), + ) + otel.SetTracerProvider(tracerProvider) + + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + )) + + return tracerProvider.Shutdown, nil +} diff --git a/pkg/querybuilder/builder.go b/pkg/querybuilder/builder.go new file mode 100644 index 0000000..272b882 --- /dev/null +++ b/pkg/querybuilder/builder.go @@ -0,0 +1,105 @@ +package querybuilder + +import ( + "fmt" + "strings" + "time" + + "github.com/fivemanage/lite/api" + "github.com/huandu/go-sqlbuilder" +) + +var topLevelFields = map[string]struct{}{ + "Timestamp": {}, + "DatasetId": {}, + "TraceId": {}, + "TeamId": {}, + "Body": {}, + "RetentionDays": {}, +} + +type Builder struct { + query *sqlbuilder.SelectBuilder +} + +func New() *Builder { + sb := sqlbuilder.NewSelectBuilder() + sb.Select("*") + sb.From("logs") + return &Builder{ + query: sb, + } +} + +func (b *Builder) Filter(filter api.DatasetFilter) *Builder { + if cond := b.buildCondition(filter); cond != "" { + b.query.Where(cond) + } + return b +} + +func (b *Builder) buildCondition(filter api.DatasetFilter) string { + if filter.Field != "" && filter.Operator != "" && filter.Value != nil { + field := filter.Field + if _, ok := topLevelFields[field]; !ok { + field = fmt.Sprintf("Attributes['%s']", filter.Field) + } + value := *filter.Value + switch filter.Operator { + case "contains": + return fmt.Sprintf("positionCaseInsensitive(%s, '%s') > 0", field, value) + case "not-contains": + return fmt.Sprintf("positionCaseInsensitive(%s, '%s') = 0", field, value) + case "starts-with": + return b.query.Like(field, value+"%") + case "ends-with": + return b.query.Like(field, "%"+value) + case "==": + return b.query.Equal(field, value) + case "!=": + return b.query.NotEqual(field, value) + case ">": + return b.query.GreaterThan(field, value) + case "<": + return b.query.LessThan(field, value) + case ">=": + return b.query.GreaterEqualThan(field, value) + case "<=": + return b.query.LessEqualThan(field, value) + case "exists": + return fmt.Sprintf("has(%s)", field) + case "not-exists": + return fmt.Sprintf("not has(%s)", field) + } + } + + if len(filter.Children) > 0 { + var conditions []string + for _, child := range filter.Children { + if cond := b.buildCondition(child); cond != "" { + conditions = append(conditions, cond) + } + } + + if len(conditions) > 0 { + logicalOperator := " AND " + if strings.ToUpper(filter.Operator) == "OR" { + logicalOperator = " OR " + } + return fmt.Sprintf("(%s)", strings.Join(conditions, logicalOperator)) + } + } + + return "" +} + +func (b *Builder) WithDateRange(startTime, endTime time.Time) *Builder { + b.query.Where(b.query.Between("Timestamp", startTime, endTime)) + return b +} + +func (b *Builder) Build(organizationID, datasetID string) (string, []interface{}) { + b.query.Where(b.query.Equal("TeamId", organizationID)) + b.query.Where(b.query.Equal("DatasetId", datasetID)) + return b.query.BuildWithFlavor(sqlbuilder.ClickHouse) +} diff --git a/pkg/storage/s3/s3.go b/pkg/storage/s3/s3.go new file mode 100644 index 0000000..ddefe2c --- /dev/null +++ b/pkg/storage/s3/s3.go @@ -0,0 +1,142 @@ +package s3 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "log/slog" + "os" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/sirupsen/logrus" +) + +const ( + MinIO = "minio" + R2 = "r2" + S3 = "s3" +) + +type Storage struct { + client *s3.Client + bucket string +} + +func New(s3Provider string) *Storage { + cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRegion("auto"), + ) + if err != nil { + log.Fatalf("failed to load default config: %v", err) + } + + // s3 will automatically use the following environment variables: + // AWS_ACCESS_KEY_ID + // AWS_SECRET_ACCESS_KEY + // these are custom: + // AWS_ENDPOINT + // AWS_BUCKET + client := s3.NewFromConfig(cfg, func(o *s3.Options) { + o.BaseEndpoint = aws.String(os.Getenv("AWS_ENDPOINT")) + + // does this only apply if minio is self hosted on the same server? + if s3Provider == MinIO { + o.UsePathStyle = true + } + }) + + bucket := os.Getenv("AWS_BUCKET") + if bucket == "" { + logrus.Fatalln("environment variable: AWS_BUCKET is not set") + } + + return &Storage{ + client: client, + bucket: bucket, + } +} + +// UploadFile will both upload and replace as long as the key is the same +func (r *Storage) UploadFile(ctx context.Context, file io.Reader, key, contentType string) error { + _, err := r.client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(r.bucket), + Key: aws.String(key), + Body: file, + ContentType: aws.String(contentType), + }) + if err != nil { + fmt.Println("Error uploading file to S3 bucket", err) + return err + } + + return nil +} + +func (r *Storage) DeleteFile() error { + _, err := r.client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{}) + if err != nil { + return err + } + + return nil +} + +func (r *Storage) CreateBucket(ctx context.Context) error { + _, err := r.client.HeadBucket(ctx, &s3.HeadBucketInput{ + Bucket: aws.String(r.bucket), + }) + if err == nil { + slog.Info(fmt.Sprintf("bucket [%s] already exists. skipping creation and policy application", r.bucket)) + return nil // Bucket exists, nothing more to do. + } + + var nfe *types.NotFound + if !errors.As(err, &nfe) { + return fmt.Errorf("failed to check for bucket: %w", err) + } + + _, err = r.client.CreateBucket(context.TODO(), &s3.CreateBucketInput{ + Bucket: aws.String(r.bucket), + }) + if err != nil { + return fmt.Errorf("failed to create bucket: %w", err) + } + + slog.Info(fmt.Sprintf("Created bucket: %s", r.bucket)) + + // i hate this bro + bucketPolicy := map[string]any{ + "Version": "2012-10-17", + "Statement": []map[string]any{ + { + "Sid": "PublicReadGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": fmt.Sprintf("arn:aws:s3:::%s/*", r.bucket), + }, + }, + } + + policyBytes, err := json.Marshal(bucketPolicy) + if err != nil { + return fmt.Errorf("failed to marshal bucket policy: %w", err) + } + + _, err = r.client.PutBucketPolicy(context.TODO(), &s3.PutBucketPolicyInput{ + Bucket: aws.String(r.bucket), + Policy: aws.String(string(policyBytes)), + }) + if err != nil { + return fmt.Errorf("failed to apply bucket policy: %w", err) + } + logrus.Info("Successfully applied public-read bucket policy.") + + return nil +} diff --git a/internal/storage/storage_strategy.go b/pkg/storage/storage_strategy.go similarity index 57% rename from internal/storage/storage_strategy.go rename to pkg/storage/storage_strategy.go index a6cb397..e9d8a60 100644 --- a/internal/storage/storage_strategy.go +++ b/pkg/storage/storage_strategy.go @@ -4,10 +4,12 @@ import ( "context" "io" - "github.com/fivemanage/lite/internal/storage/s3" + "github.com/fivemanage/lite/pkg/storage/s3" + "github.com/spf13/viper" ) type StorageLayer interface { + CreateBucket(context.Context) error UploadFile(context.Context, io.Reader, string, string) error DeleteFile() error } @@ -15,7 +17,8 @@ type StorageLayer interface { func New(provider string) StorageLayer { switch provider { case "s3": - return s3.New() + s3Provider := viper.GetString("s3-provider") + return s3.New(s3Provider) } return nil diff --git a/web/.env.template b/web/.env.template new file mode 100644 index 0000000..93ba05b --- /dev/null +++ b/web/.env.template @@ -0,0 +1,2 @@ +VITE_DOMAIN= +VITE_VERSION=dev diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs deleted file mode 100644 index d6c9537..0000000 --- a/web/.eslintrc.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/web/.gitignore b/web/.gitignore index a547bf3..019e1dc 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,5 +1,4 @@ # Logs -logs *.log npm-debug.log* yarn-debug.log* diff --git a/web/components.json b/web/components.json index 8121514..189a6a4 100644 --- a/web/components.json +++ b/web/components.json @@ -1,17 +1,21 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "src/global.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "src/components", - "utils": "src/lib/utils" - } -} + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/global.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 0000000..0c1ce3f --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,25 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["off"], + }, + }, +); diff --git a/web/package.json b/web/package.json index 9a3dd6d..bde46c8 100644 --- a/web/package.json +++ b/web/package.json @@ -5,47 +5,74 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", "build:dev": "vite build --watch", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { - "@hookform/resolvers": "^3.10.0", - "@radix-ui/react-dialog": "^1.1.5", + "@date-fns/utc": "^2.1.0", + "@hookform/resolvers": "^5.1.1", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.1", + "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.14", "@radix-ui/react-label": "^2.1.1", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.1.7", + "@tailwindcss/vite": "^4.1.6", "@tanstack/react-query": "^5.59.15", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.12", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "geist": "^1.3.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "dayjs": "^1.11.13", + "jotai": "^2.12.5", + "lodash": "^4.17.21", "lucide-react": "^0.428.0", "react": "^19.0.0", + "react-autosuggest": "10.1.0", + "react-day-picker": "^9.8.0", "react-dom": "^19.0.0", - "react-hook-form": "^7.54.2", - "react-router": "^7.1.5", - "tailwind-merge": "^2.5.2", + "react-dropzone": "^14.3.8", + "react-hook-form": "^7.60.0", + "react-router": "^7.6.0", + "sonner": "^2.0.6", + "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.24.1" + "zod": "^4.0.0" }, "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/js": "^9.27.0", "@tailwindcss/typography": "^0.5.16", + "@types/lodash": "^4.17.20", "@types/node": "^22.4.0", "@types/react": "^19.0.8", + "@types/react-autosuggest": "^10.1.11", "@types/react-dom": "^19.0.3", - "@typescript-eslint/eslint-plugin": "^7.1.1", - "@typescript-eslint/parser": "^7.1.1", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@vitejs/plugin-react-swc": "^3.8.0", "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", + "eslint": "^9.27.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.1.0", "postcss": "^8.4.38", - "tailwindcss": "^3.4.1", + "tailwindcss": "^4.1.7", + "tw-animate-css": "^1.3.0", "typescript": "^5.5.3", + "typescript-eslint": "^8.32.1", "vite": "^6.1.0" - } + }, + "packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad" } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 76f5f6a..b980afe 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,296 +8,402 @@ importers: .: dependencies: + '@date-fns/utc': + specifier: ^2.1.0 + version: 2.1.0 '@hookform/resolvers': - specifier: ^3.10.0 - version: 3.10.0(react-hook-form@7.54.2(react@19.0.0)) + specifier: ^5.1.1 + version: 5.1.1(react-hook-form@7.60.0(react@19.1.0)) + '@radix-ui/react-avatar': + specifier: ^1.1.10 + version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-checkbox': + specifier: ^1.3.1 + version: 1.3.2(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': - specifier: ^1.1.5 - version: 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.15(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-hover-card': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-label': specifier: ^2.1.1 - version: 2.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popover': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-radio-group': + specifier: ^1.3.7 + version: 1.3.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-separator': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': - specifier: ^1.1.0 - version: 1.1.0(@types/react@19.0.8)(react@19.0.0) + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-toggle': + specifier: ^1.1.9 + version: 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle-group': + specifier: ^1.1.10 + version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.1.7 - version: 1.1.7(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tailwindcss/vite': + specifier: ^4.1.6 + version: 4.1.7(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)) '@tanstack/react-query': specifier: ^5.59.15 - version: 5.59.15(react@19.0.0) + version: 5.76.2(react@19.1.0) + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-virtual': + specifier: ^3.13.12 + version: 3.13.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) class-variance-authority: specifier: ^0.7.0 - version: 0.7.0 + version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 - geist: - specifier: ^1.3.1 - version: 1.3.1(next@15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + jotai: + specifier: ^2.12.5 + version: 2.12.5(@types/react@19.1.5)(react@19.1.0) + lodash: + specifier: ^4.17.21 + version: 4.17.21 lucide-react: specifier: ^0.428.0 - version: 0.428.0(react@19.0.0) + version: 0.428.0(react@19.1.0) react: specifier: ^19.0.0 - version: 19.0.0 + version: 19.1.0 + react-autosuggest: + specifier: 10.1.0 + version: 10.1.0(react@19.1.0) + react-day-picker: + specifier: ^9.8.0 + version: 9.8.0(react@19.1.0) react-dom: specifier: ^19.0.0 - version: 19.0.0(react@19.0.0) + version: 19.1.0(react@19.1.0) + react-dropzone: + specifier: ^14.3.8 + version: 14.3.8(react@19.1.0) react-hook-form: - specifier: ^7.54.2 - version: 7.54.2(react@19.0.0) + specifier: ^7.60.0 + version: 7.60.0(react@19.1.0) react-router: - specifier: ^7.1.5 - version: 7.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^7.6.0 + version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + sonner: + specifier: ^2.0.6 + version: 2.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwind-merge: - specifier: ^2.5.2 - version: 2.5.2 + specifier: ^3.3.0 + version: 3.3.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.1) + version: 1.0.7(tailwindcss@4.1.7) zod: - specifier: ^3.24.1 - version: 3.24.1 + specifier: ^4.0.0 + version: 4.0.5 devDependencies: + '@eslint/compat': + specifier: ^1.2.9 + version: 1.2.9(eslint@9.27.0(jiti@2.4.2)) + '@eslint/js': + specifier: ^9.27.0 + version: 9.27.0 '@tailwindcss/typography': specifier: ^0.5.16 - version: 0.5.16(tailwindcss@3.4.1) + version: 0.5.16(tailwindcss@4.1.7) + '@types/lodash': + specifier: ^4.17.20 + version: 4.17.20 '@types/node': specifier: ^22.4.0 - version: 22.4.0 + version: 22.15.21 '@types/react': specifier: ^19.0.8 - version: 19.0.8 + version: 19.1.5 + '@types/react-autosuggest': + specifier: ^10.1.11 + version: 10.1.11 '@types/react-dom': specifier: ^19.0.3 - version: 19.0.3(@types/react@19.0.8) + version: 19.1.5(@types/react@19.1.5) '@typescript-eslint/eslint-plugin': - specifier: ^7.1.1 - version: 7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.32.1 + version: 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^7.1.1 - version: 7.1.1(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.32.1 + version: 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@vitejs/plugin-react-swc': specifier: ^3.8.0 - version: 3.8.0(@swc/helpers@0.5.15)(vite@6.1.0(@types/node@22.4.0)(jiti@1.21.0)) + version: 3.10.0(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)) autoprefixer: specifier: ^10.4.19 - version: 10.4.19(postcss@8.4.38) + version: 10.4.21(postcss@8.5.3) eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.27.0 + version: 9.27.0(jiti@2.4.2) eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.57.0) + specifier: ^5.2.0 + version: 5.2.0(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-react-refresh: - specifier: ^0.4.5 - version: 0.4.5(eslint@8.57.0) + specifier: ^0.4.20 + version: 0.4.20(eslint@9.27.0(jiti@2.4.2)) + globals: + specifier: ^16.1.0 + version: 16.1.0 postcss: specifier: ^8.4.38 - version: 8.4.38 + version: 8.5.3 tailwindcss: - specifier: ^3.4.1 - version: 3.4.1 + specifier: ^4.1.7 + version: 4.1.7 + tw-animate-css: + specifier: ^1.3.0 + version: 1.3.0 typescript: specifier: ^5.5.3 - version: 5.5.4 + version: 5.8.3 + typescript-eslint: + specifier: ^8.32.1 + version: 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) vite: specifier: ^6.1.0 - version: 6.1.0(@types/node@22.4.0)(jiti@1.21.0) + version: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1) packages: - '@aashutoshrathi/word-wrap@1.2.6': - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} + '@date-fns/tz@1.2.0': + resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} - '@emnapi/runtime@1.3.1': - resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@date-fns/utc@2.1.0': + resolution: {integrity: sha512-176grgAgU2U303rD2/vcOmNg0kGPbhzckuH1TEP2al7n0AQipZIy9P15usd2TKQCG1g+E1jX/ZVQSzs4sUDwgA==} - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/compat@1.2.9': + resolution: {integrity: sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.2': + resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.27.0': + resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/core@1.6.9': - resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + '@eslint/plugin-kit@0.3.1': + resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.0': + resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==} + + '@floating-ui/dom@1.7.0': + resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==} '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} @@ -308,133 +414,37 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@hookform/resolvers@3.10.0': - resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} + '@hookform/resolvers@5.1.1': + resolution: {integrity: sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==} peerDependencies: - react-hook-form: ^7.0.0 + react-hook-form: ^7.55.0 - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.2': - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} - - '@img/sharp-darwin-arm64@0.33.5': - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.33.5': - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.33.5': - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.33.5': - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-s390x@0.33.5': - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.33.5': - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.33.5': - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': @@ -445,63 +455,12 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@15.2.2': - resolution: {integrity: sha512-yWgopCfA9XDR8ZH3taB5nRKtKJ1Q5fYsTOuYkzIIoS8TJ0UAUKAGF73JnGszbjk2ufAQDj6mDdgsJAFx5CLtYQ==} - - '@next/swc-darwin-arm64@15.2.2': - resolution: {integrity: sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-x64@15.2.2': - resolution: {integrity: sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@15.2.2': - resolution: {integrity: sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-arm64-musl@15.2.2': - resolution: {integrity: sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-x64-gnu@15.2.2': - resolution: {integrity: sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-linux-x64-musl@15.2.2': - resolution: {integrity: sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-win32-arm64-msvc@15.2.2': - resolution: {integrity: sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@15.2.2': - resolution: {integrity: sha512-52nWy65S/R6/kejz3jpvHAjZDPKIbEQu4x9jDBzmB9jJfuOy5rspjKu4u77+fI4M/WzLXrrQd57hlFGzz1ubcQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -514,15 +473,11 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@radix-ui/primitive@1.1.1': - resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} - '@radix-ui/react-arrow@1.1.1': - resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==} + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -534,8 +489,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.2': - resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -547,8 +502,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.2': - resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + '@radix-ui/react-checkbox@1.3.2': + resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -560,17 +515,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.0': - resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-compose-refs@1.1.1': - resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -578,8 +537,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.1': - resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -587,8 +546,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.5': - resolution: {integrity: sha512-LaO3e5h/NOEL4OfXjxD43k9Dx+vn+8n+PCFt6uhX/BADFflllyv3WJG6rgvvSVBxpTch938Qq/LGc2MMxipXPw==} + '@radix-ui/react-dialog@1.1.14': + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -600,8 +559,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.0': - resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -609,8 +568,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.4': - resolution: {integrity: sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==} + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -622,8 +581,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.5': - resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} + '@radix-ui/react-dropdown-menu@2.1.15': + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -635,30 +594,30 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.6': - resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-focus-guards@1.1.1': - resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-focus-scope@1.1.1': - resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==} + '@radix-ui/react-hover-card@1.1.14': + resolution: {integrity: sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -670,30 +629,30 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-scope@1.1.2': - resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-id@1.1.0': - resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-label@2.1.1': - resolution: {integrity: sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==} + '@radix-ui/react-menu@2.1.15': + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -705,8 +664,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.6': - resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + '@radix-ui/react-popover@1.1.14': + resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -718,8 +677,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.1': - resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==} + '@radix-ui/react-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -731,8 +690,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.2': - resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -744,8 +703,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.3': - resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} + '@radix-ui/react-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -757,8 +716,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.4': - resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -770,8 +729,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.2': - resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + '@radix-ui/react-radio-group@1.3.7': + resolution: {integrity: sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -783,8 +742,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.1': - resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} + '@radix-ui/react-roving-focus@1.1.10': + resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -796,8 +755,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.2': - resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -809,8 +768,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.2': - resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-toggle-group@1.1.10': + resolution: {integrity: sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -822,8 +790,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.1': - resolution: {integrity: sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==} + '@radix-ui/react-toggle@1.1.9': + resolution: {integrity: sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -835,17 +803,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.1.0': - resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + '@radix-ui/react-tooltip@1.2.7': + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-slot@1.1.1': - resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -853,8 +825,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-slot@1.1.2': - resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -862,21 +834,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-tooltip@1.1.7': - resolution: {integrity: sha512-ss0s80BC0+g0+Zc53MvilcnTYSOi4mSuFWBPYPuTOFGjx+pUU+ZrmamMNwS56t8MTFlniA5ocjd4jYm/CdhbOg==} + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-use-callback-ref@1.1.0': - resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -884,8 +852,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.1.0': - resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -893,8 +861,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.0': - resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -902,8 +870,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.0': - resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -911,8 +879,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.1.0': - resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -920,8 +888,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.1.0': - resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -929,8 +897,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-visually-hidden@1.1.1': - resolution: {integrity: sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==} + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -942,169 +910,180 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.1.0': - resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rolldown/pluginutils@1.0.0-beta.9': + resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} - '@rollup/rollup-android-arm-eabi@4.34.6': - resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} + '@rollup/rollup-android-arm-eabi@4.41.0': + resolution: {integrity: sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.34.6': - resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} + '@rollup/rollup-android-arm64@4.41.0': + resolution: {integrity: sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.34.6': - resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} + '@rollup/rollup-darwin-arm64@4.41.0': + resolution: {integrity: sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.6': - resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} + '@rollup/rollup-darwin-x64@4.41.0': + resolution: {integrity: sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.34.6': - resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} + '@rollup/rollup-freebsd-arm64@4.41.0': + resolution: {integrity: sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.6': - resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} + '@rollup/rollup-freebsd-x64@4.41.0': + resolution: {integrity: sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': - resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.6': - resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} + '@rollup/rollup-linux-arm-musleabihf@4.41.0': + resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.6': - resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} + '@rollup/rollup-linux-arm64-gnu@4.41.0': + resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.6': - resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} + '@rollup/rollup-linux-arm64-musl@4.41.0': + resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': - resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': + resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': - resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': + resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.6': - resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} + '@rollup/rollup-linux-riscv64-gnu@4.41.0': + resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.41.0': + resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.6': - resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} + '@rollup/rollup-linux-s390x-gnu@4.41.0': + resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.6': - resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} + '@rollup/rollup-linux-x64-gnu@4.41.0': + resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.6': - resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} + '@rollup/rollup-linux-x64-musl@4.41.0': + resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.34.6': - resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} + '@rollup/rollup-win32-arm64-msvc@4.41.0': + resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.6': - resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} + '@rollup/rollup-win32-ia32-msvc@4.41.0': + resolution: {integrity: sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.6': - resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} + '@rollup/rollup-win32-x64-msvc@4.41.0': + resolution: {integrity: sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==} cpu: [x64] os: [win32] - '@swc/core-darwin-arm64@1.10.15': - resolution: {integrity: sha512-zFdZ6/yHqMCPk7OhLFqHy/MQ1EqJhcZMpNHd1gXYT7VRU3FaqvvKETrUlG3VYl65McPC7AhMRfXPyJ0JO/jARQ==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@swc/core-darwin-arm64@1.11.29': + resolution: {integrity: sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.10.15': - resolution: {integrity: sha512-8g4yiQwbr8fxOOjKXdot0dEkE5zgE8uNZudLy/ZyAhiwiZ8pbJ8/wVrDOu6dqbX7FBXAoDnvZ7fwN1jk4C8jdA==} + '@swc/core-darwin-x64@1.11.29': + resolution: {integrity: sha512-S3eTo/KYFk+76cWJRgX30hylN5XkSmjYtCBnM4jPLYn7L6zWYEPajsFLmruQEiTEDUg0gBEWLMNyUeghtswouw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.10.15': - resolution: {integrity: sha512-rl+eVOltl2+7WXOnvmWBpMgh6aO13G5x0U0g8hjwlmD6ku3Y9iRcThpOhm7IytMEarUp5pQxItNoPq+VUGjVHg==} + '@swc/core-linux-arm-gnueabihf@1.11.29': + resolution: {integrity: sha512-o9gdshbzkUMG6azldHdmKklcfrcMx+a23d/2qHQHPDLUPAN+Trd+sDQUYArK5Fcm7TlpG4sczz95ghN0DMkM7g==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.10.15': - resolution: {integrity: sha512-qxWEQeyAJMWJqjaN4hi58WMpPdt3Tn0biSK9CYRegQtvZWCbewr6v2agtSu5AZ2rudeH6OfCWAMDQQeSgn6PJQ==} + '@swc/core-linux-arm64-gnu@1.11.29': + resolution: {integrity: sha512-sLoaciOgUKQF1KX9T6hPGzvhOQaJn+3DHy4LOHeXhQqvBgr+7QcZ+hl4uixPKTzxk6hy6Hb0QOvQEdBAAR1gXw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.10.15': - resolution: {integrity: sha512-QcELd9/+HjZx0WCxRrKcyKGWTiQ0485kFb5w8waxcSNd0d9Lgk4EFfWWVyvIb5gIHpDQmhrgzI/yRaWQX4YSZQ==} + '@swc/core-linux-arm64-musl@1.11.29': + resolution: {integrity: sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.10.15': - resolution: {integrity: sha512-S1+ZEEn3+a/MiMeQqQypbwTGoBG8/sPoCvpNbk+uValyygT+jSn3U0xVr45FbukpmMB+NhBMqfedMLqKA0QnJA==} + '@swc/core-linux-x64-gnu@1.11.29': + resolution: {integrity: sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.10.15': - resolution: {integrity: sha512-qW+H9g/2zTJ4jP7NDw4VAALY0ZlNEKzYsEoSj/HKi7k3tYEHjMzsxjfsY9I8WZCft23bBdV3RTCPoxCshaj1CQ==} + '@swc/core-linux-x64-musl@1.11.29': + resolution: {integrity: sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.10.15': - resolution: {integrity: sha512-AhRB11aA6LxjIqut+mg7qsu/7soQDmbK6MKR9nP3hgBszpqtXbRba58lr24xIbBCMr+dpo6kgEapWt+t5Po6Zg==} + '@swc/core-win32-arm64-msvc@1.11.29': + resolution: {integrity: sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.10.15': - resolution: {integrity: sha512-UGdh430TQwbDn6KjgvRTg1fO022sbQ4yCCHUev0+5B8uoBwi9a89qAz3emy2m56C8TXxUoihW9Y9OMfaRwPXUw==} + '@swc/core-win32-ia32-msvc@1.11.29': + resolution: {integrity: sha512-h+NjOrbqdRBYr5ItmStmQt6x3tnhqgwbj9YxdGPepbTDamFv7vFnhZR0YfB3jz3UKJ8H3uGJ65Zw1VsC+xpFkg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.10.15': - resolution: {integrity: sha512-XJzBCqO1m929qbJsOG7FZXQWX26TnEoMctS3QjuCoyBmkHxxQmZsy78KjMes1aomTcKHCyFYgrRGWgVmk7tT4Q==} + '@swc/core-win32-x64-msvc@1.11.29': + resolution: {integrity: sha512-Q8cs2BDV9wqDvqobkXOYdC+pLUSEpX/KvI0Dgfun1F+LzuLotRFuDhrvkU9ETJA6OnD2+Fn/ieHgloiKA/Mn/g==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.10.15': - resolution: {integrity: sha512-/iFeQuNaGdK7mfJbQcObhAhsMqLT7qgMYl7jX2GEIO+VDTejESpzAyKwaMeYXExN8D6e5BRHBCe7M5YlsuzjDA==} + '@swc/core@1.11.29': + resolution: {integrity: sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==} engines: {node: '>=10'} peerDependencies: - '@swc/helpers': '*' + '@swc/helpers': '>=0.5.17' peerDependenciesMeta: '@swc/helpers': optional: true @@ -1112,111 +1091,204 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/types@0.1.21': + resolution: {integrity: sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==} + + '@tailwindcss/node@4.1.7': + resolution: {integrity: sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==} + + '@tailwindcss/oxide-android-arm64@4.1.7': + resolution: {integrity: sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.7': + resolution: {integrity: sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.7': + resolution: {integrity: sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.7': + resolution: {integrity: sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7': + resolution: {integrity: sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.7': + resolution: {integrity: sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.7': + resolution: {integrity: sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.7': + resolution: {integrity: sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.7': + resolution: {integrity: sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.7': + resolution: {integrity: sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.7': + resolution: {integrity: sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.7': + resolution: {integrity: sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] - '@swc/types@0.1.17': - resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==} + '@tailwindcss/oxide@4.1.7': + resolution: {integrity: sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==} + engines: {node: '>= 10'} '@tailwindcss/typography@0.5.16': resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - '@tanstack/query-core@5.59.13': - resolution: {integrity: sha512-Oou0bBu/P8+oYjXsJQ11j+gcpLAMpqW42UlokQYEz4dE7+hOtVO9rVuolJKgEccqzvyFzqX4/zZWY+R/v1wVsQ==} + '@tailwindcss/vite@4.1.7': + resolution: {integrity: sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@tanstack/query-core@5.76.2': + resolution: {integrity: sha512-PFGwWh5ss9cJQ67l6bZ7hqXbisX2gy13G2jP+VGY1bgdbCfOMWh6UBVnN62QbFXro6CCoX9hYzTnZHr6Rz00YQ==} - '@tanstack/react-query@5.59.15': - resolution: {integrity: sha512-QbVlAkTI78wB4Mqgf2RDmgC0AOiJqer2c5k9STOOSXGv1S6ZkY37r/6UpE8DbQ2Du0ohsdoXgFNEyv+4eDoPEw==} + '@tanstack/react-query@5.76.2': + resolution: {integrity: sha512-rGkWberCrFdIxMdvSAJM/UOKeu0O/JVTbMmfhQoJpiU9Uq0EDx2EMCadnNuJWbXR4smDA2t7DY3NKkYFmDVS5A==} peerDependencies: react: ^18 || ^19 - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-virtual@3.13.12': + resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.4.0': - resolution: {integrity: sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - '@types/react-dom@19.0.3': - resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} + '@types/node@22.15.21': + resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} + + '@types/react-autosuggest@10.1.11': + resolution: {integrity: sha512-lneJrX/5TZJzKHPJ6UuUjsh9OfeyQHKYEVHyBh5Y7LeRbCZxyIsjBmpxdPy1iH++Ger0qcyW+phPpYH+g3naLA==} + + '@types/react-dom@19.1.5': + resolution: {integrity: sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==} peerDependencies: '@types/react': ^19.0.0 - '@types/react@19.0.8': - resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} + '@types/react@19.1.5': + resolution: {integrity: sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==} - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@typescript-eslint/eslint-plugin@7.1.1': - resolution: {integrity: sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/eslint-plugin@8.32.1': + resolution: {integrity: sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@7.1.1': - resolution: {integrity: sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@8.32.1': + resolution: {integrity: sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@7.1.1': - resolution: {integrity: sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.32.1': + resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@7.1.1': - resolution: {integrity: sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/type-utils@8.32.1': + resolution: {integrity: sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@7.1.1': - resolution: {integrity: sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@8.32.1': + resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.1.1': - resolution: {integrity: sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/typescript-estree@8.32.1': + resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@7.1.1': - resolution: {integrity: sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/utils@8.32.1': + resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/visitor-keys@7.1.1': - resolution: {integrity: sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==} - engines: {node: ^16.0.0 || >=18.0.0} + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@typescript-eslint/visitor-keys@8.32.1': + resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-react-swc@3.8.0': - resolution: {integrity: sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw==} + '@vitejs/plugin-react-swc@3.10.0': + resolution: {integrity: sha512-ZmkdHw3wo/o/Rk05YsXZs/DJAfY2CdQ5DUAjoWji+PEr+hYADdGMCGgEAILbiKj+CjspBTuTACBcWDrmC8AUfw==} peerDependencies: vite: ^4 || ^5 || ^6 @@ -1225,53 +1297,31 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} hasBin: true ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.4: - resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} + attr-accept@2.2.5: + resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} + engines: {node: '>=4'} - autoprefixer@10.4.19: - resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -1280,62 +1330,49 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.0: - resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + browserslist@4.24.5: + resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - - caniuse-lite@1.0.30001600: - resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - class-variance-authority@0.7.0: - resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} - - client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} - clsx@2.0.0: - resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} - engines: {node: '>=6'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmdk@1.1.1: + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1343,17 +1380,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1361,8 +1387,8 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} cssesc@3.0.0: @@ -1373,8 +1399,17 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + date-fns-jalali@4.1.0-0: + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1385,82 +1420,75 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + electron-to-chromium@1.5.157: + resolution: {integrity: sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.4.717: - resolution: {integrity: sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} engines: {node: '>=18'} hasBin: true - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-react-hooks@4.6.0: - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} engines: {node: '>=10'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-refresh@0.4.5: - resolution: {integrity: sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==} + eslint-plugin-react-refresh@0.4.20: + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} peerDependencies: - eslint: '>=7' + eslint: '>=8.40' - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.27.0: + resolution: {integrity: sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1478,8 +1506,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -1488,51 +1516,48 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-selector@2.1.2: + resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==} + engines: {node: '>= 12'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - geist@1.3.1: - resolution: {integrity: sha512-Q4gC1pBVPN+D579pBaz0TRRnGA4p9UK6elDY/xizXdFk/g4EKR5g0I+4p/Kj6gM0SajDBZ/0FvDV9ey9ud7BWw==} - peerDependencies: - next: '>=13.2.0' - get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -1545,21 +1570,16 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@16.1.0: + resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==} + engines: {node: '>=18'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1568,46 +1588,26 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1616,21 +1616,28 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} - - jiti@1.21.0: - resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jotai@2.12.5: + resolution: {integrity: sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1651,16 +1658,69 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] - lilconfig@3.1.1: - resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} - engines: {node: '>=14'} + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -1675,102 +1735,77 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lru-cache@10.2.0: - resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} - engines: {node: 14 || >=16.14} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true lucide-react@0.428.0: resolution: {integrity: sha512-rGrzslfEcgqwh+TLBC5qJ8wvVIXhLvAIXVFKNHndYyb1utSxxn9rXOC+1CNJLi6yNOooyPqIs6+3YCp6uSiEvg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@15.2.2: - resolution: {integrity: sha512-dgp8Kcx5XZRjMw2KNwBtUzhngRaURPioxoNIVl5BOyJbhi9CUgEtKDO7fx5wh8Z8vOVX1nYZ9meawJoRrlASYA==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + object-assign@3.0.0: + resolution: {integrity: sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==} + engines: {node: '>=0.10.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} p-limit@3.1.0: @@ -1789,28 +1824,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1818,71 +1835,28 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-nested@6.0.1: - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} - postcss-selector-parser@6.0.16: - resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} - engines: {node: '>=4'} - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.2: - resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1890,17 +1864,37 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@19.0.0: - resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + react-autosuggest@10.1.0: + resolution: {integrity: sha512-/azBHmc6z/31s/lBf6irxPf/7eejQdR0IqnZUzjdSibtlS8+Rw/R79pgDAo6Ft5QqCUTyEQ+f0FhL+1olDQ8OA==} + peerDependencies: + react: '>=16.3.0' + + react-day-picker@9.8.0: + resolution: {integrity: sha512-E0yhhg7R+pdgbl/2toTb0xBhsEAtmAx1l7qjIWYfcxOy8w4rTSVfbtBoSzVVhPwKP/5E9iL38LivzoE3AQDhCQ==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8.0' + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: - react: ^19.0.0 + react: ^19.1.0 - react-hook-form@7.54.2: - resolution: {integrity: sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==} + react-dropzone@14.3.8: + resolution: {integrity: sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==} + engines: {node: '>= 10.13'} + peerDependencies: + react: '>= 16.8 || 18.0.0' + + react-hook-form@7.60.0: + resolution: {integrity: sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -1911,8 +1905,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.6.3: - resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} + react-remove-scroll@2.7.0: + resolution: {integrity: sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg==} engines: {node: '>=10'} peerDependencies: '@types/react': '*' @@ -1921,8 +1915,8 @@ packages: '@types/react': optional: true - react-router@7.1.5: - resolution: {integrity: sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==} + react-router@7.6.0: + resolution: {integrity: sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -1941,60 +1935,45 @@ packages: '@types/react': optional: true - react@19.0.0: - resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} - engines: {node: '>=0.10.0'} - - read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + react-themeable@1.1.0: + resolution: {integrity: sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - - rollup@4.34.6: - resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} + rollup@4.41.0: + resolution: {integrity: sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - scheduler@0.25.0: - resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} - semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true + section-iterator@2.0.0: + resolution: {integrity: sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ==} - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} - sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -2004,135 +1983,84 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} + sonner@2.0.6: + resolution: {integrity: sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tailwind-merge@2.5.2: - resolution: {integrity: sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==} + tailwind-merge@3.3.0: + resolution: {integrity: sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@3.4.1: - resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} - engines: {node: '>=14.0.0'} - hasBin: true + tailwindcss@4.1.7: + resolution: {integrity: sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' - - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + typescript: '>=4.8.4' tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo-stream@2.4.0: - resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} + tw-animate-css@1.3.0: + resolution: {integrity: sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==} type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} + typescript-eslint@8.32.1: + resolution: {integrity: sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.6: - resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - update-browserslist-db@1.0.13: - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2160,11 +2088,16 @@ packages: '@types/react': optional: true + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@6.1.0: - resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2208,300 +2141,210 @@ packages: engines: {node: '>= 8'} hasBin: true - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} - yaml@2.4.1: - resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} - engines: {node: '>= 14'} - hasBin: true + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + zod@4.0.5: + resolution: {integrity: sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==} snapshots: - '@aashutoshrathi/word-wrap@1.2.6': {} + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 - '@alloc/quick-lru@5.2.0': {} + '@date-fns/tz@1.2.0': {} - '@emnapi/runtime@1.3.1': - dependencies: - tslib: 2.8.1 - optional: true + '@date-fns/utc@2.1.0': {} - '@esbuild/aix-ppc64@0.24.2': + '@esbuild/aix-ppc64@0.25.4': optional: true - '@esbuild/android-arm64@0.24.2': + '@esbuild/android-arm64@0.25.4': optional: true - '@esbuild/android-arm@0.24.2': + '@esbuild/android-arm@0.25.4': optional: true - '@esbuild/android-x64@0.24.2': + '@esbuild/android-x64@0.25.4': optional: true - '@esbuild/darwin-arm64@0.24.2': + '@esbuild/darwin-arm64@0.25.4': optional: true - '@esbuild/darwin-x64@0.24.2': + '@esbuild/darwin-x64@0.25.4': optional: true - '@esbuild/freebsd-arm64@0.24.2': + '@esbuild/freebsd-arm64@0.25.4': optional: true - '@esbuild/freebsd-x64@0.24.2': + '@esbuild/freebsd-x64@0.25.4': optional: true - '@esbuild/linux-arm64@0.24.2': + '@esbuild/linux-arm64@0.25.4': optional: true - '@esbuild/linux-arm@0.24.2': + '@esbuild/linux-arm@0.25.4': optional: true - '@esbuild/linux-ia32@0.24.2': + '@esbuild/linux-ia32@0.25.4': optional: true - '@esbuild/linux-loong64@0.24.2': + '@esbuild/linux-loong64@0.25.4': optional: true - '@esbuild/linux-mips64el@0.24.2': + '@esbuild/linux-mips64el@0.25.4': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/linux-ppc64@0.25.4': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/linux-riscv64@0.25.4': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/linux-s390x@0.25.4': optional: true - '@esbuild/linux-x64@0.24.2': + '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.24.2': + '@esbuild/netbsd-arm64@0.25.4': optional: true - '@esbuild/netbsd-x64@0.24.2': + '@esbuild/netbsd-x64@0.25.4': optional: true - '@esbuild/openbsd-arm64@0.24.2': + '@esbuild/openbsd-arm64@0.25.4': optional: true - '@esbuild/openbsd-x64@0.24.2': + '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/sunos-x64@0.24.2': + '@esbuild/sunos-x64@0.25.4': optional: true - '@esbuild/win32-arm64@0.24.2': + '@esbuild/win32-arm64@0.25.4': optional: true - '@esbuild/win32-ia32@0.24.2': + '@esbuild/win32-ia32@0.25.4': optional: true - '@esbuild/win32-x64@0.24.2': + '@esbuild/win32-x64@0.25.4': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.27.0(jiti@2.4.2))': dependencies: - eslint: 8.57.0 + eslint: 9.27.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.0': {} + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.2.9(eslint@9.27.0(jiti@2.4.2))': + optionalDependencies: + eslint: 9.27.0(jiti@2.4.2) + + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.2': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 - '@eslint/eslintrc@2.1.4': + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.1 - import-fresh: 3.3.0 + debug: 4.4.1 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@9.27.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.1': + dependencies: + '@eslint/core': 0.14.0 + levn: 0.4.1 - '@floating-ui/core@1.6.9': + '@floating-ui/core@1.7.0': dependencies: '@floating-ui/utils': 0.2.9 - '@floating-ui/dom@1.6.13': + '@floating-ui/dom@1.7.0': dependencies: - '@floating-ui/core': 1.6.9 + '@floating-ui/core': 1.7.0 '@floating-ui/utils': 0.2.9 - '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@floating-ui/react-dom@2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@floating-ui/dom': 1.6.13 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@floating-ui/dom': 1.7.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) '@floating-ui/utils@0.2.9': {} - '@hookform/resolvers@3.10.0(react-hook-form@7.54.2(react@19.0.0))': + '@hookform/resolvers@5.1.1(react-hook-form@7.60.0(react@19.1.0))': dependencies: - react-hook-form: 7.54.2(react@19.0.0) + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.60.0(react@19.1.0) - '@humanwhocodes/config-array@0.11.14': + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.2': {} - - '@img/sharp-darwin-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 - optional: true - - '@img/sharp-darwin-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.0.5': - optional: true + '@humanwhocodes/retry@0.3.1': {} - '@img/sharp-libvips-linux-s390x@1.0.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.0.4': - optional: true + '@humanwhocodes/retry@0.4.3': {} - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - optional: true - - '@img/sharp-linux-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 - optional: true - - '@img/sharp-linux-arm@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 - optional: true - - '@img/sharp-linux-s390x@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 - optional: true - - '@img/sharp-linux-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - optional: true - - '@img/sharp-wasm32@0.33.5': - dependencies: - '@emnapi/runtime': 1.3.1 - optional: true - - '@img/sharp-win32-ia32@0.33.5': - optional: true - - '@img/sharp-win32-x64@0.33.5': - optional: true - - '@isaacs/cliui@8.0.2': + '@isaacs/fs-minipass@4.0.1': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + minipass: 7.1.2 - '@jridgewell/gen-mapping@0.3.5': + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - - '@next/env@15.2.2': {} - - '@next/swc-darwin-arm64@15.2.2': - optional: true - - '@next/swc-darwin-x64@15.2.2': - optional: true - - '@next/swc-linux-arm64-gnu@15.2.2': - optional: true - - '@next/swc-linux-arm64-musl@15.2.2': - optional: true - - '@next/swc-linux-x64-gnu@15.2.2': - optional: true - - '@next/swc-linux-x64-musl@15.2.2': - optional: true - - '@next/swc-win32-arm64-msvc@15.2.2': - optional: true - - '@next/swc-win32-x64-msvc@15.2.2': - optional: true + '@jridgewell/sourcemap-codec': 1.5.0 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2513,651 +2356,773 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - - '@pkgjs/parseargs@0.11.0': - optional: true + fastq: 1.19.1 - '@radix-ui/primitive@1.1.1': {} + '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-collection@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-compose-refs@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.0.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.5)(react@19.1.0)': dependencies: - react: 19.0.0 + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-context@1.1.1(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.1.5)(react@19.1.0)': dependencies: - react: 19.0.0 + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - - '@radix-ui/react-dialog@1.1.5(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.8)(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-direction@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.0.0 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.0(@types/react@19.1.5)(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-dismissable-layer@1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-menu': 2.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.0.0 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.5)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.5 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-hover-card@1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-id@1.1.1(@types/react@19.1.5)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.5 + + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-menu@2.1.15(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.0(@types/react@19.1.5)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.0(@types/react@19.1.5)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-radio-group@1.3.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) + + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-id@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-toggle-group@1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-menu@2.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.8)(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-toggle@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) - - '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-separator@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-slot@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-slot@1.1.1(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-slot@1.1.2(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.8 - - '@radix-ui/react-tooltip@1.1.7(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@types/react': 19.1.5 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - react: 19.0.0 + '@radix-ui/rect': 1.1.1 + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.5)(react@19.1.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.5)(react@19.1.0) + react: 19.1.0 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.8)(react@19.0.0)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 + '@types/react-dom': 19.1.5(@types/react@19.1.5) - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.8)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.8 + '@radix-ui/rect@1.1.1': {} - '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.8)(react@19.0.0)': - dependencies: - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.8 + '@rolldown/pluginutils@1.0.0-beta.9': {} + + '@rollup/rollup-android-arm-eabi@4.41.0': + optional: true + + '@rollup/rollup-android-arm64@4.41.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.41.0': + optional: true + + '@rollup/rollup-darwin-x64@4.41.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.41.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.41.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + optional: true - '@radix-ui/react-use-size@1.1.0(@types/react@19.0.8)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.8 + '@rollup/rollup-linux-arm-musleabihf@4.41.0': + optional: true - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.8 - '@types/react-dom': 19.0.3(@types/react@19.0.8) + '@rollup/rollup-linux-arm64-gnu@4.41.0': + optional: true - '@radix-ui/rect@1.1.0': {} + '@rollup/rollup-linux-arm64-musl@4.41.0': + optional: true - '@rollup/rollup-android-arm-eabi@4.34.6': + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': optional: true - '@rollup/rollup-android-arm64@4.34.6': + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': optional: true - '@rollup/rollup-darwin-arm64@4.34.6': + '@rollup/rollup-linux-riscv64-gnu@4.41.0': optional: true - '@rollup/rollup-darwin-x64@4.34.6': + '@rollup/rollup-linux-riscv64-musl@4.41.0': optional: true - '@rollup/rollup-freebsd-arm64@4.34.6': + '@rollup/rollup-linux-s390x-gnu@4.41.0': optional: true - '@rollup/rollup-freebsd-x64@4.34.6': + '@rollup/rollup-linux-x64-gnu@4.41.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': + '@rollup/rollup-linux-x64-musl@4.41.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.6': + '@rollup/rollup-win32-arm64-msvc@4.41.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.6': + '@rollup/rollup-win32-ia32-msvc@4.41.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.6': + '@rollup/rollup-win32-x64-msvc@4.41.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': + '@standard-schema/utils@0.3.0': {} + + '@swc/core-darwin-arm64@1.11.29': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': + '@swc/core-darwin-x64@1.11.29': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.6': + '@swc/core-linux-arm-gnueabihf@1.11.29': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.6': + '@swc/core-linux-arm64-gnu@1.11.29': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.6': + '@swc/core-linux-arm64-musl@1.11.29': optional: true - '@rollup/rollup-linux-x64-musl@4.34.6': + '@swc/core-linux-x64-gnu@1.11.29': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.6': + '@swc/core-linux-x64-musl@1.11.29': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.6': + '@swc/core-win32-arm64-msvc@1.11.29': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.6': + '@swc/core-win32-ia32-msvc@1.11.29': optional: true - '@swc/core-darwin-arm64@1.10.15': + '@swc/core-win32-x64-msvc@1.11.29': optional: true - '@swc/core-darwin-x64@1.10.15': + '@swc/core@1.11.29': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.21 + optionalDependencies: + '@swc/core-darwin-arm64': 1.11.29 + '@swc/core-darwin-x64': 1.11.29 + '@swc/core-linux-arm-gnueabihf': 1.11.29 + '@swc/core-linux-arm64-gnu': 1.11.29 + '@swc/core-linux-arm64-musl': 1.11.29 + '@swc/core-linux-x64-gnu': 1.11.29 + '@swc/core-linux-x64-musl': 1.11.29 + '@swc/core-win32-arm64-msvc': 1.11.29 + '@swc/core-win32-ia32-msvc': 1.11.29 + '@swc/core-win32-x64-msvc': 1.11.29 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.21': + dependencies: + '@swc/counter': 0.1.3 + + '@tailwindcss/node@4.1.7': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.7 + + '@tailwindcss/oxide-android-arm64@4.1.7': optional: true - '@swc/core-linux-arm-gnueabihf@1.10.15': + '@tailwindcss/oxide-darwin-arm64@4.1.7': optional: true - '@swc/core-linux-arm64-gnu@1.10.15': + '@tailwindcss/oxide-darwin-x64@4.1.7': optional: true - '@swc/core-linux-arm64-musl@1.10.15': + '@tailwindcss/oxide-freebsd-x64@4.1.7': optional: true - '@swc/core-linux-x64-gnu@1.10.15': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7': optional: true - '@swc/core-linux-x64-musl@1.10.15': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.7': optional: true - '@swc/core-win32-arm64-msvc@1.10.15': + '@tailwindcss/oxide-linux-arm64-musl@4.1.7': optional: true - '@swc/core-win32-ia32-msvc@1.10.15': + '@tailwindcss/oxide-linux-x64-gnu@4.1.7': optional: true - '@swc/core-win32-x64-msvc@1.10.15': + '@tailwindcss/oxide-linux-x64-musl@4.1.7': optional: true - '@swc/core@1.10.15(@swc/helpers@0.5.15)': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.17 - optionalDependencies: - '@swc/core-darwin-arm64': 1.10.15 - '@swc/core-darwin-x64': 1.10.15 - '@swc/core-linux-arm-gnueabihf': 1.10.15 - '@swc/core-linux-arm64-gnu': 1.10.15 - '@swc/core-linux-arm64-musl': 1.10.15 - '@swc/core-linux-x64-gnu': 1.10.15 - '@swc/core-linux-x64-musl': 1.10.15 - '@swc/core-win32-arm64-msvc': 1.10.15 - '@swc/core-win32-ia32-msvc': 1.10.15 - '@swc/core-win32-x64-msvc': 1.10.15 - '@swc/helpers': 0.5.15 + '@tailwindcss/oxide-wasm32-wasi@4.1.7': + optional: true - '@swc/counter@0.1.3': {} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.7': + optional: true - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 + '@tailwindcss/oxide-win32-x64-msvc@4.1.7': + optional: true - '@swc/types@0.1.17': + '@tailwindcss/oxide@4.1.7': dependencies: - '@swc/counter': 0.1.3 + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.7 + '@tailwindcss/oxide-darwin-arm64': 4.1.7 + '@tailwindcss/oxide-darwin-x64': 4.1.7 + '@tailwindcss/oxide-freebsd-x64': 4.1.7 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.7 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.7 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.7 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.7 + '@tailwindcss/oxide-linux-x64-musl': 4.1.7 + '@tailwindcss/oxide-wasm32-wasi': 4.1.7 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.7 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.7 - '@tailwindcss/typography@0.5.16(tailwindcss@3.4.1)': + '@tailwindcss/typography@0.5.16(tailwindcss@4.1.7)': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.1 + tailwindcss: 4.1.7 + + '@tailwindcss/vite@4.1.7(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.7 + '@tailwindcss/oxide': 4.1.7 + tailwindcss: 4.1.7 + vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1) + + '@tanstack/query-core@5.76.2': {} + + '@tanstack/react-query@5.76.2(react@19.1.0)': + dependencies: + '@tanstack/query-core': 5.76.2 + react: 19.1.0 - '@tanstack/query-core@5.59.13': {} + '@tanstack/react-table@8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@tanstack/react-query@5.59.15(react@19.0.0)': + '@tanstack/react-virtual@3.13.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@tanstack/query-core': 5.59.13 - react: 19.0.0 + '@tanstack/virtual-core': 3.13.12 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@tanstack/table-core@8.21.3': {} - '@types/cookie@0.6.0': {} + '@tanstack/virtual-core@3.13.12': {} - '@types/estree@1.0.6': {} + '@types/estree@1.0.7': {} '@types/json-schema@7.0.15': {} - '@types/node@22.4.0': + '@types/lodash@4.17.20': {} + + '@types/node@22.15.21': dependencies: - undici-types: 6.19.6 + undici-types: 6.21.0 - '@types/react-dom@19.0.3(@types/react@19.0.8)': + '@types/react-autosuggest@10.1.11': dependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - '@types/react@19.0.8': + '@types/react-dom@19.1.5(@types/react@19.1.5)': dependencies: - csstype: 3.1.3 + '@types/react': 19.1.5 - '@types/semver@7.5.8': {} + '@types/react@19.1.5': + dependencies: + csstype: 3.1.3 - '@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.1.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 7.1.1 - '@typescript-eslint/type-utils': 7.1.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.1.1 - debug: 4.3.4 - eslint: 8.57.0 + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/type-utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 + eslint: 9.27.0(jiti@2.4.2) graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 7.0.4 natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 7.1.1 - '@typescript-eslint/types': 7.1.1 - '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.1.1 - debug: 4.3.4 - eslint: 8.57.0 - optionalDependencies: - typescript: 5.5.4 + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.1.1': + '@typescript-eslint/scope-manager@8.32.1': dependencies: - '@typescript-eslint/types': 7.1.1 - '@typescript-eslint/visitor-keys': 7.1.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 - '@typescript-eslint/type-utils@7.1.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.5.4) - '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.27.0(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.1.1': {} + '@typescript-eslint/types@8.32.1': {} - '@typescript-eslint/typescript-estree@7.1.1(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.32.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 7.1.1 - '@typescript-eslint/visitor-keys': 7.1.1 - debug: 4.3.4 - globby: 11.1.0 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 + debug: 4.4.1 + fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.1.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.1.1 - '@typescript-eslint/types': 7.1.1 - '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.5.4) - eslint: 8.57.0 - semver: 7.6.0 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@7.1.1': + '@typescript-eslint/visitor-keys@8.32.1': dependencies: - '@typescript-eslint/types': 7.1.1 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.32.1 + eslint-visitor-keys: 4.2.0 - '@ungap/structured-clone@1.2.0': {} - - '@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@6.1.0(@types/node@22.4.0)(jiti@1.21.0))': + '@vitejs/plugin-react-swc@3.10.0(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1))': dependencies: - '@swc/core': 1.10.15(@swc/helpers@0.5.15) - vite: 6.1.0(@types/node@22.4.0)(jiti@1.21.0) + '@rolldown/pluginutils': 1.0.0-beta.9 + '@swc/core': 1.11.29 + vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1) transitivePeerDependencies: - '@swc/helpers' - acorn-jsx@5.3.2(acorn@8.11.3): + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: - acorn: 8.11.3 + acorn: 8.14.1 - acorn@8.11.3: {} + acorn@8.14.1: {} ajv@6.12.6: dependencies: @@ -3166,47 +3131,30 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-regex@5.0.1: {} - - ansi-regex@6.0.1: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@5.0.2: {} - argparse@2.0.1: {} - aria-hidden@1.2.4: + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 - array-union@2.1.0: {} + attr-accept@2.2.5: {} - autoprefixer@10.4.19(postcss@8.4.38): + autoprefixer@10.4.21(postcss@8.5.3): dependencies: - browserslist: 4.23.0 - caniuse-lite: 1.0.30001600 + browserslist: 4.24.5 + caniuse-lite: 1.0.30001718 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.38 + picocolors: 1.1.1 + postcss: 8.5.3 postcss-value-parser: 4.2.0 balanced-match@1.0.2: {} - binary-extensions@2.3.0: {} - brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -3216,79 +3164,57 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: - dependencies: - fill-range: 7.0.1 - - browserslist@4.23.0: + braces@3.0.3: dependencies: - caniuse-lite: 1.0.30001600 - electron-to-chromium: 1.4.717 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) + fill-range: 7.1.1 - busboy@1.6.0: + browserslist@4.24.5: dependencies: - streamsearch: 1.1.0 + caniuse-lite: 1.0.30001718 + electron-to-chromium: 1.5.157 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.5) callsites@3.1.0: {} - camelcase-css@2.0.1: {} - - caniuse-lite@1.0.30001600: {} + caniuse-lite@1.0.30001718: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + chownr@3.0.0: {} - class-variance-authority@0.7.0: + class-variance-authority@0.7.1: dependencies: - clsx: 2.0.0 - - client-only@0.0.1: {} - - clsx@2.0.0: {} + clsx: 2.1.1 clsx@2.1.1: {} - color-convert@2.0.1: + cmdk@1.1.1(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.5)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' - color-string@1.9.1: + color-convert@2.0.1: dependencies: color-name: 1.1.4 - simple-swizzle: 0.2.2 - optional: true - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - optional: true - commander@4.1.1: {} + color-name@1.1.4: {} concat-map@0.0.1: {} cookie@1.0.2: {} - cross-spawn@7.0.3: + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -3298,134 +3224,129 @@ snapshots: csstype@3.1.3: {} - debug@4.3.4: - dependencies: - ms: 2.1.2 - - deep-is@0.1.4: {} - - detect-libc@2.0.3: - optional: true - - detect-node-es@1.1.0: {} - - didyoumean@1.2.2: {} + date-fns-jalali@4.1.0-0: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 + date-fns@4.1.0: {} - dlv@1.1.3: {} + dayjs@1.11.13: {} - doctrine@3.0.0: + debug@4.4.1: dependencies: - esutils: 2.0.3 - - eastasianwidth@0.2.0: {} + ms: 2.1.3 - electron-to-chromium@1.4.717: {} + deep-is@0.1.4: {} - emoji-regex@8.0.0: {} + detect-libc@2.0.4: {} - emoji-regex@9.2.2: {} + detect-node-es@1.1.0: {} - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - - escalade@3.1.2: {} + electron-to-chromium@1.5.157: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + es6-promise@4.2.8: {} + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): + eslint-plugin-react-hooks@5.2.0(eslint@9.27.0(jiti@2.4.2)): dependencies: - eslint: 8.57.0 + eslint: 9.27.0(jiti@2.4.2) - eslint-plugin-react-refresh@0.4.5(eslint@8.57.0): + eslint-plugin-react-refresh@0.4.20(eslint@9.27.0(jiti@2.4.2)): dependencies: - eslint: 8.57.0 + eslint: 9.27.0(jiti@2.4.2) - eslint-scope@7.2.2: + eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint@8.57.0: + eslint-visitor-keys@4.2.0: {} + + eslint@9.27.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.2 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.27.0 + '@eslint/plugin-kit': 0.3.1 + '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 + cross-spawn: 7.0.6 + debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - 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 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@10.3.0: dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 - esquery@1.5.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -3439,27 +3360,35 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.8 fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fastq@1.17.1: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 - file-entry-cache@6.0.1: + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-selector@2.1.2: dependencies: - flat-cache: 3.2.0 + tslib: 2.8.1 - fill-range@7.0.1: + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -3468,32 +3397,18 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: - flatted: 3.3.1 + flatted: 3.3.3 keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.1: {} - foreground-child@3.1.1: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 + flatted@3.3.3: {} fraction.js@4.3.7: {} - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - geist@1.3.1(next@15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): - dependencies: - next: 15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - get-nonce@1.0.1: {} glob-parent@5.1.2: @@ -3504,92 +3419,45 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.3.10: - dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + globals@14.0.0: {} - globals@13.24.0: - dependencies: - type-fest: 0.20.2 + globals@16.1.0: {} - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 + graceful-fs@4.2.11: {} graphemer@1.4.0: {} has-flag@4.0.0: {} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 + ignore@5.3.2: {} - ignore@5.3.1: {} + ignore@7.0.4: {} - import-fresh@3.3.0: + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - is-arrayish@0.3.2: - optional: true - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-core-module@2.13.1: - dependencies: - hasown: 2.0.2 - is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} - is-path-inside@3.0.3: {} - isexe@2.0.0: {} - jackspeak@2.3.6: - dependencies: - '@isaacs/cliui': 8.0.2 + jiti@2.4.2: {} + + jotai@2.12.5(@types/react@19.1.5)(react@19.1.0): optionalDependencies: - '@pkgjs/parseargs': 0.11.0 + '@types/react': 19.1.5 + react: 19.1.0 - jiti@1.21.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: dependencies: @@ -3610,11 +3478,50 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lilconfig@2.1.0: {} + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true - lilconfig@3.1.1: {} + lightningcss-win32-x64-msvc@1.30.1: + optional: true - lines-and-columns@1.2.4: {} + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 locate-path@6.0.0: dependencies: @@ -3626,94 +3533,65 @@ snapshots: lodash.merge@4.6.2: {} - lru-cache@10.2.0: {} + lodash@4.17.21: {} - lru-cache@6.0.0: + loose-envify@1.4.0: dependencies: - yallist: 4.0.0 + js-tokens: 4.0.0 - lucide-react@0.428.0(react@19.0.0): + lucide-react@0.428.0(react@19.1.0): dependencies: - react: 19.0.0 + react: 19.1.0 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 merge2@1.4.1: {} - micromatch@4.0.5: + micromatch@4.0.8: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.3: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - minipass@7.0.4: {} + minipass@7.1.2: {} - ms@2.1.2: {} - - mz@2.7.0: + minizlib@3.0.2: dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 + minipass: 7.1.2 - nanoid@3.3.7: {} + mkdirp@3.0.1: {} - nanoid@3.3.8: {} + ms@2.1.3: {} - natural-compare@1.4.0: {} - - next@15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@next/env': 15.2.2 - '@swc/counter': 0.1.3 - '@swc/helpers': 0.5.15 - busboy: 1.6.0 - caniuse-lite: 1.0.30001600 - postcss: 8.4.31 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - styled-jsx: 5.1.6(react@19.0.0) - optionalDependencies: - '@next/swc-darwin-arm64': 15.2.2 - '@next/swc-darwin-x64': 15.2.2 - '@next/swc-linux-arm64-gnu': 15.2.2 - '@next/swc-linux-arm64-musl': 15.2.2 - '@next/swc-linux-x64-gnu': 15.2.2 - '@next/swc-linux-x64-musl': 15.2.2 - '@next/swc-win32-arm64-msvc': 15.2.2 - '@next/swc-win32-x64-msvc': 15.2.2 - sharp: 0.33.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros + nanoid@3.3.11: {} - node-releases@2.0.14: {} + natural-compare@1.4.0: {} - normalize-path@3.0.0: {} + node-releases@2.0.19: {} normalize-range@0.1.2: {} - object-assign@4.1.1: {} - - object-hash@3.0.0: {} + object-assign@3.0.0: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 + object-assign@4.1.1: {} - optionator@0.9.3: + optionator@0.9.4: dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.5 p-limit@3.1.0: dependencies: @@ -3729,225 +3607,157 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - - path-scurry@1.10.1: - dependencies: - lru-cache: 10.2.0 - minipass: 7.0.4 - - path-type@4.0.0: {} - - picocolors@1.0.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} - pify@2.3.0: {} - - pirates@4.0.6: {} - - postcss-import@15.1.0(postcss@8.4.38): - dependencies: - postcss: 8.4.38 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.8 - - postcss-js@4.0.1(postcss@8.4.38): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.38 - - postcss-load-config@4.0.2(postcss@8.4.38): - dependencies: - lilconfig: 3.1.1 - yaml: 2.4.1 - optionalDependencies: - postcss: 8.4.38 - - postcss-nested@6.0.1(postcss@8.4.38): - dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.0.16 + picomatch@4.0.2: {} postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@6.0.16: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-value-parser@4.2.0: {} - postcss@8.4.31: + postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.38: - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.2.0 + prelude-ls@1.2.1: {} - postcss@8.5.2: + prop-types@15.8.1: dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 punycode@2.3.1: {} queue-microtask@1.2.3: {} - react-dom@19.0.0(react@19.0.0): + react-autosuggest@10.1.0(react@19.1.0): + dependencies: + es6-promise: 4.2.8 + prop-types: 15.8.1 + react: 19.1.0 + react-themeable: 1.1.0 + section-iterator: 2.0.0 + shallow-equal: 1.2.1 + + react-day-picker@9.8.0(react@19.1.0): + dependencies: + '@date-fns/tz': 1.2.0 + date-fns: 4.1.0 + date-fns-jalali: 4.1.0-0 + react: 19.1.0 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-dropzone@14.3.8(react@19.1.0): dependencies: - react: 19.0.0 - scheduler: 0.25.0 + attr-accept: 2.2.5 + file-selector: 2.1.2 + prop-types: 15.8.1 + react: 19.1.0 - react-hook-form@7.54.2(react@19.0.0): + react-hook-form@7.60.0(react@19.1.0): dependencies: - react: 19.0.0 + react: 19.1.0 + + react-is@16.13.1: {} - react-remove-scroll-bar@2.3.8(@types/react@19.0.8)(react@19.0.0): + react-remove-scroll-bar@2.3.8(@types/react@19.1.5)(react@19.1.0): dependencies: - react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@19.0.8)(react@19.0.0) + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.1.5)(react@19.1.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - react-remove-scroll@2.6.3(@types/react@19.0.8)(react@19.0.0): + react-remove-scroll@2.7.0(@types/react@19.1.5)(react@19.1.0): dependencies: - react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.8)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.8)(react@19.0.0) + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.1.5)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.5)(react@19.1.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.8)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@19.0.8)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@19.1.5)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.1.5)(react@19.1.0) optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - react-router@7.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-router@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@types/cookie': 0.6.0 cookie: 1.0.2 - react: 19.0.0 + react: 19.1.0 set-cookie-parser: 2.7.1 - turbo-stream: 2.4.0 optionalDependencies: - react-dom: 19.0.0(react@19.0.0) + react-dom: 19.1.0(react@19.1.0) - react-style-singleton@2.2.3(@types/react@19.0.8)(react@19.0.0): + react-style-singleton@2.2.3(@types/react@19.1.5)(react@19.1.0): dependencies: get-nonce: 1.0.1 - react: 19.0.0 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - react@19.0.0: {} - - read-cache@1.0.0: + react-themeable@1.1.0: dependencies: - pify: 2.3.0 + object-assign: 3.0.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 + react@19.1.0: {} resolve-from@4.0.0: {} - resolve@1.22.8: - dependencies: - is-core-module: 2.13.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.0.4: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - - rollup@4.34.6: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.6 - '@rollup/rollup-android-arm64': 4.34.6 - '@rollup/rollup-darwin-arm64': 4.34.6 - '@rollup/rollup-darwin-x64': 4.34.6 - '@rollup/rollup-freebsd-arm64': 4.34.6 - '@rollup/rollup-freebsd-x64': 4.34.6 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.6 - '@rollup/rollup-linux-arm-musleabihf': 4.34.6 - '@rollup/rollup-linux-arm64-gnu': 4.34.6 - '@rollup/rollup-linux-arm64-musl': 4.34.6 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.6 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6 - '@rollup/rollup-linux-riscv64-gnu': 4.34.6 - '@rollup/rollup-linux-s390x-gnu': 4.34.6 - '@rollup/rollup-linux-x64-gnu': 4.34.6 - '@rollup/rollup-linux-x64-musl': 4.34.6 - '@rollup/rollup-win32-arm64-msvc': 4.34.6 - '@rollup/rollup-win32-ia32-msvc': 4.34.6 - '@rollup/rollup-win32-x64-msvc': 4.34.6 + reusify@1.1.0: {} + + rollup@4.41.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.41.0 + '@rollup/rollup-android-arm64': 4.41.0 + '@rollup/rollup-darwin-arm64': 4.41.0 + '@rollup/rollup-darwin-x64': 4.41.0 + '@rollup/rollup-freebsd-arm64': 4.41.0 + '@rollup/rollup-freebsd-x64': 4.41.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.0 + '@rollup/rollup-linux-arm-musleabihf': 4.41.0 + '@rollup/rollup-linux-arm64-gnu': 4.41.0 + '@rollup/rollup-linux-arm64-musl': 4.41.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-musl': 4.41.0 + '@rollup/rollup-linux-s390x-gnu': 4.41.0 + '@rollup/rollup-linux-x64-gnu': 4.41.0 + '@rollup/rollup-linux-x64-musl': 4.41.0 + '@rollup/rollup-win32-arm64-msvc': 4.41.0 + '@rollup/rollup-win32-ia32-msvc': 4.41.0 + '@rollup/rollup-win32-x64-msvc': 4.41.0 fsevents: 2.3.3 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - scheduler@0.25.0: {} + scheduler@0.26.0: {} - semver@7.6.0: - dependencies: - lru-cache: 6.0.0 + section-iterator@2.0.0: {} - semver@7.7.1: - optional: true + semver@7.7.2: {} set-cookie-parser@2.7.1: {} - sharp@0.33.5: - dependencies: - color: 4.2.3 - detect-libc: 2.0.3 - semver: 7.7.1 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 - optional: true + shallow-equal@1.2.1: {} shebang-command@2.0.0: dependencies: @@ -3955,190 +3765,126 @@ snapshots: shebang-regex@3.0.0: {} - signal-exit@4.1.0: {} - - simple-swizzle@0.2.2: + sonner@2.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - is-arrayish: 0.3.2 - optional: true - - slash@3.0.0: {} - - source-map-js@1.2.0: {} + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) source-map-js@1.2.1: {} - streamsearch@1.1.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.0.1 - strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(react@19.0.0): - dependencies: - client-only: 0.0.1 - react: 19.0.0 - - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - commander: 4.1.1 - glob: 10.3.10 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} + tailwind-merge@3.3.0: {} - tailwind-merge@2.5.2: {} - - tailwindcss-animate@1.0.7(tailwindcss@3.4.1): + tailwindcss-animate@1.0.7(tailwindcss@4.1.7): dependencies: - tailwindcss: 3.4.1 + tailwindcss: 4.1.7 - tailwindcss@3.4.1: - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.2 - 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.38 - postcss-import: 15.1.0(postcss@8.4.38) - postcss-js: 4.0.1(postcss@8.4.38) - postcss-load-config: 4.0.2(postcss@8.4.38) - postcss-nested: 6.0.1(postcss@8.4.38) - postcss-selector-parser: 6.0.16 - resolve: 1.22.8 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node + tailwindcss@4.1.7: {} - text-table@0.2.0: {} + tapable@2.2.2: {} - thenify-all@1.6.0: + tar@7.4.3: dependencies: - thenify: 3.3.1 + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 - thenify@3.3.1: + tinyglobby@0.2.13: dependencies: - any-promise: 1.3.0 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - ts-api-utils@1.3.0(typescript@5.5.4): + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: - typescript: 5.5.4 - - ts-interface-checker@0.1.13: {} + typescript: 5.8.3 tslib@2.8.1: {} - turbo-stream@2.4.0: {} + tw-animate-css@1.3.0: {} type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - type-fest@0.20.2: {} + typescript-eslint@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color - typescript@5.5.4: {} + typescript@5.8.3: {} - undici-types@6.19.6: {} + undici-types@6.21.0: {} - update-browserslist-db@1.0.13(browserslist@4.23.0): + update-browserslist-db@1.1.3(browserslist@4.24.5): dependencies: - browserslist: 4.23.0 - escalade: 3.1.2 - picocolors: 1.0.0 + browserslist: 4.24.5 + escalade: 3.2.0 + picocolors: 1.1.1 uri-js@4.4.1: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.0.8)(react@19.0.0): + use-callback-ref@1.3.3(@types/react@19.1.5)(react@19.1.0): dependencies: - react: 19.0.0 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 - use-sidecar@1.1.3(@types/react@19.0.8)(react@19.0.0): + use-sidecar@1.1.3(@types/react@19.1.5)(react@19.1.0): dependencies: detect-node-es: 1.1.0 - react: 19.0.0 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.8 + '@types/react': 19.1.5 + + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 util-deprecate@1.0.2: {} - vite@6.1.0(@types/node@22.4.0)(jiti@1.21.0): + vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1): dependencies: - esbuild: 0.24.2 - postcss: 8.5.2 - rollup: 4.34.6 + esbuild: 0.25.4 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.41.0 + tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 22.4.0 + '@types/node': 22.15.21 fsevents: 2.3.3 - jiti: 1.21.0 + jiti: 2.4.2 + lightningcss: 1.30.1 which@2.0.2: dependencies: isexe: 2.0.0 - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - wrappy@1.0.2: {} - - yallist@4.0.0: {} + word-wrap@1.2.5: {} - yaml@2.4.1: {} + yallist@5.0.0: {} yocto-queue@0.1.0: {} - zod@3.24.1: {} + zod@4.0.5: {} diff --git a/web/postcss.config.js b/web/postcss.config.js deleted file mode 100644 index 7b75c83..0000000 --- a/web/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/web/public/logos/logo-black.png b/web/public/logos/logo-black.png new file mode 100644 index 0000000..38ce1c1 Binary files /dev/null and b/web/public/logos/logo-black.png differ diff --git a/web/public/logos/logo-white.png b/web/public/logos/logo-white.png new file mode 100644 index 0000000..7648e9e Binary files /dev/null and b/web/public/logos/logo-white.png differ diff --git a/web/src/App.tsx b/web/src/App.tsx index c4537bd..430037d 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,10 +1,18 @@ +import { lazy } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { BrowserRouter, Route, Routes } from "react-router"; +import { BrowserRouter, Navigate, Route, Routes } from "react-router"; import { AuthRoute } from "./features/auth/routes/AuthRoute"; import { AppDashboard } from "./features/app/routes/AppDashboard"; import { AppLayout } from "./features/app/components/AppLayout"; -import { TokensRoute } from "./features/tokens/routes/TokensRoute"; import { ThemeProvider } from "./components/theme/ThemeProvider"; +import { ProtectedRoute } from "./features/auth/routes/ProtectedRoute"; +import { NewOrganizationRoute } from "./features/organizations/routes/NewOrganizationRoute"; +import { OrganizationSelectRoute } from "./features/organizations/routes/OrganizationSelectRoute"; +import { Toaster } from "sonner"; +const StorageRoute = lazy(() => import("./features/files/StorageRoute")); +const TokensRoute = lazy(() => import("./features/tokens/routes/TokensRoute")); +const DatasetRoute = lazy(() => import("./features/logs/routes/dataset-route")); +const LogsRoute = lazy(() => import("./features/logs/routes/logs-route")); const queryClient = new QueryClient(); @@ -13,12 +21,28 @@ function App() { + + 404} /> + } /> } /> - }> - } /> - - } /> + + } /> + }> + } + /> + }> + } /> + } /> + } /> + + } /> + } /> + + + diff --git a/web/src/components/data/DataTable.tsx b/web/src/components/data/DataTable.tsx new file mode 100644 index 0000000..7aae38b --- /dev/null +++ b/web/src/components/data/DataTable.tsx @@ -0,0 +1,172 @@ +import { + type ColumnDef, + type PaginationState, + type Row, + type RowSelectionState, + flexRender, + getCoreRowModel, + getPaginationRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { useEffect, useRef, useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { useLocation, useSearchParams, useNavigate } from "react-router"; +import { Asset } from "@/typings/asset"; +//import { DataDeleteDialog } from "./DataDeleteDialog"; + +interface DataTableProps { + data: T[]; + columns: ColumnDef[]; + totalCount: number; + isLoading: boolean; + onDelete: (rows: Row[]) => Promise; + rowSelection: RowSelectionState; + setRowSelection: React.Dispatch>; + showDeleteModal: boolean; + setShowDeleteModal: React.Dispatch>; +} + +export function DataTable({ + data, + totalCount, + columns, + rowSelection, + setRowSelection, +}: DataTableProps) { + const router = useNavigate(); + const pathname = useLocation(); + const [searchParams] = useSearchParams(); + + const [pagination, setPagination] = useState({ + pageSize: 20, + pageIndex: Number(searchParams.get("page")) || 0, + }); + + const tableContainerRef = useRef(null); + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setRowSelection, + pageCount: Math.ceil(totalCount / 20), + manualPagination: true, + state: { + pagination, + rowSelection: rowSelection, + }, + }); + + /*const handleDeleteSelection = async () => { + const rows = table.getFilteredSelectedRowModel().rows; + await onDelete(rows); + }; */ + + useEffect(() => { + if (pagination) { + const params = new URLSearchParams(searchParams); + params.set("page", `${pagination.pageIndex}`); + //router.replace(`${pathname}?${params.toString()}`); + } + }, [pagination, router, searchParams, pathname]); + + return ( +
+ {/*table.getFilteredSelectedRowModel().rows.length ? ( + + selectedRows={table.getFilteredSelectedRowModel().rows} + showModal={showDeleteModal} + setShowDeleteModal={setShowDeleteModal} + onDelete={handleDeleteSelection} + isLoading={isLoading} + /> + ) : null */} +
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+ + + {table.getHeaderGroups()?.map((headerGroup) => ( + + {headerGroup.headers?.map((header) => { + return ( + + {header.isPlaceholder ? null : ( +
+ {flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
+ )} +
+ ); + })} +
+ ))} +
+ + {table.getRowModel().rows.length ? ( + table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ); + })} + + ); + }) + ) : ( + + + No results. + + + )} + +
+
+
+ + +
+
+ ); +} diff --git a/web/src/components/data/DataTableSkeleton.tsx b/web/src/components/data/DataTableSkeleton.tsx new file mode 100644 index 0000000..0d9c1d6 --- /dev/null +++ b/web/src/components/data/DataTableSkeleton.tsx @@ -0,0 +1,167 @@ +import { cn } from "@/lib/utils"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +interface DataTableSkeletonProps extends React.HTMLAttributes { + /** + * The number of columns in the table. + * @type number + */ + columnCount: number; + + /** + * The number of rows in the table. + * @default 10 + * @type number | undefined + */ + rowCount?: number; + + /** + * The number of searchable columns in the table. + * @default 0 + * @type number | undefined + */ + searchableColumnCount?: number; + + /** + * The number of filterable columns in the table. + * @default 0 + * @type number | undefined + */ + filterableColumnCount?: number; + + /** + * Flag to show the table view options. + * @default undefined + * @type boolean | undefined + */ + showViewOptions?: boolean; + + /** + * The width of each cell in the table. + * The length of the array should be equal to the columnCount. + * Any valid CSS width value is accepted. + * @default ["auto"] + * @type string[] | undefined + */ + cellWidths?: string[]; + + /** + * Flag to show the pagination bar. + * @default true + * @type boolean | undefined + */ + withPagination?: boolean; + + /** + * Flag to prevent the table cells from shrinking. + * @default false + * @type boolean | undefined + */ + shrinkZero?: boolean; +} + +export function DataTableSkeleton(props: DataTableSkeletonProps) { + const { + columnCount, + rowCount = 10, + searchableColumnCount = 0, + filterableColumnCount = 0, + showViewOptions = true, + cellWidths = ["auto"], + withPagination = true, + shrinkZero = false, + className, + ...skeletonProps + } = props; + + return ( +
+
+
+ {searchableColumnCount > 0 + ? Array.from({ length: searchableColumnCount }).map((_, i) => ( + + )) + : null} + {filterableColumnCount > 0 + ? Array.from({ length: filterableColumnCount }).map((_, i) => ( + + )) + : null} +
+ {showViewOptions ? ( + + ) : null} +
+
+ + + {Array.from({ length: 1 }).map((_, i) => ( + + {Array.from({ length: columnCount }).map((_, j) => ( + + + + ))} + + ))} + + + {Array.from({ length: rowCount }).map((_, i) => ( + + {Array.from({ length: columnCount }).map((_, j) => ( + + + + ))} + + ))} + +
+
+ {withPagination ? ( +
+ +
+
+ + +
+
+ +
+
+ + + + +
+
+
+ ) : null} +
+ ); +} diff --git a/web/src/components/theme/ModeToggle.tsx b/web/src/components/theme/ModeToggle.tsx index ea8d44f..741e65e 100644 --- a/web/src/components/theme/ModeToggle.tsx +++ b/web/src/components/theme/ModeToggle.tsx @@ -5,8 +5,8 @@ import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "../ui/DropdownMenu"; -import { Button } from "../ui/Button"; +} from "../ui/dropdown-menu"; +import { Button } from "../ui/button"; import { useTheme } from "./useTheme"; export function ModeToggle() { @@ -15,9 +15,9 @@ export function ModeToggle() { return ( - diff --git a/web/src/components/ui/Breadcrumb.tsx b/web/src/components/ui/Breadcrumb.tsx deleted file mode 100644 index ecfc6a4..0000000 --- a/web/src/components/ui/Breadcrumb.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import * as React from "react"; -import { Slot } from "@radix-ui/react-slot"; -import { ChevronRight, MoreHorizontal } from "lucide-react"; - -import { cn } from "@/lib/utils"; - -const Breadcrumb = React.forwardRef< - HTMLElement, - React.ComponentPropsWithoutRef<"nav"> & { - separator?: React.ReactNode; - } ->(({ ...props }, ref) =>