diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..e928b86 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +// This configuration only applies to the package manager root. +/** @type {import("eslint").Linter.Config} */ +module.exports = { + ignorePatterns: ["apps/**", "packages/**"], + extends: ["@repo/eslint-config/library.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff12bc4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Dependencies +node_modules +.pnp +.pnp.js + +# Local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Testing +coverage + +# Turbo +.turbo + +# Vercel +.vercel + +# Build Outputs +.next/ +out/ +build +dist + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Misc +.DS_Store +*.pem + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Environments +.venv \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e69de29 diff --git a/.vscode/next-fast-turbo.code-workspace b/.vscode/next-fast-turbo.code-workspace new file mode 100644 index 0000000..89704e5 --- /dev/null +++ b/.vscode/next-fast-turbo.code-workspace @@ -0,0 +1,33 @@ +{ + "folders": [ + { + "name": "Frontend", + "path": "../apps/web" + }, + { + "name": "API", + "path": "../apps/api" + }, + { + "name": "Documentation", + "path": "../apps/docs" + }, + { + "name": "Root", + "path": "../" + } + ], + "extensions": { + "recommendations": ["joshx.workspace-terminals"] + }, + "launch": { + "version": "0.2.0", + "configurations": [], + "compounds": [ + { + "name": "Launch Frontend and Backend", + "configurations": ["Next.js: Chrome", "Python: FastAPI"] + } + ] + } +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3e5c2e2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ], + "python.testing.pytestArgs": ["apps"], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "cSpell.enableFiletypes": ["mdx"] +} diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENCE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9d379d --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +

Next-Fast-Turbo

+ +

+ Introduction | + Features | + Tech Stack | + Getting started | + Contributing +

+

+ + Documentation + +

+
+ +## Introduction + +Next-Fast-Turbo is an open-source project scaffold. It's designed to be easy to get up and running both locally and in production. It's a monorepo that includes a Next.js frontend, a FastAPI backend, and a fully built and annotated Mintlify documentation site. It's built with TypeScript, and includes ESLint and Prettier for code quality. It's also set up to use Vercel for deployments and Remote Caching. + +View the live frontend at [next-fast-turbo-web.vercel.app](https://next-fast-turbo-web.vercel.app/) and the live backend at [next-fast-turbo-api.vercel.app](https://next-fast-turbo-api.vercel.app/). + +## Features + +- Pre-configured [FastAPI backend](https://next-fast-turbo.mintlify.app/documentation/configuration/fastapi) +- Pre-configured [Next.js frontend](https://next-fast-turbo.mintlify.app/documentation/configuration/nextjs) +- Pre-configured [Mintlify documentation site](https://next-fast-turbo.mintlify.app/documentation/configuration/docs) + +## Tech Stack + +- [Next.js](https://nextjs.org/) – Frontend Framework +- [Tailwind](https://tailwindcss.com/) – CSS Framework +- [ShadCN UI](https://ui.shadcn.com/) – UI Components +- [FastAPI](https://fastapi.tiangolo.com/) – Python Backend +- [Mintlify](https://mintlify.com/) – Documentation +- [Supabase](https://supabase.com/) – Database +- [Turborepo](https://turbo.build/repo) – Monorepo +- [Vercel](https://vercel.com/) – deployments + +## Getting started + +Next-Fast-Turbo is designed to be cloned and modified to each project. For more information on getting started, [view the documentation](https://next-fast-turbo.mintlify.app/documentation/introduction). + +## Contributing + +Contributions are welcome. Here's how you can contribute: + +- [Open an issue](https://github.com/cording12/next-fast-turbo/issues) if you believe you've encountered a bug. +- Follow the [local development guide](https://next-fast-turbo.mintlify.app/documentation/local-development) to get your local dev environment set up. +- Make a [pull request](https://github.com/cording12/next-fast-turbo/pulls) to add new features/make quality-of-life improvements/fix bugs. + +## License + +Next-Fast-Turbo is open-source under the GNU General Public License Version 3 (GPLv3) or any later version. diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 0000000..db3b808 --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1,4 @@ +DB_URL=supabase_url +DB_API_KEY=supabase_api_key +DB_EMAIl=email_address +DB_PASSWORD=password \ No newline at end of file diff --git a/apps/api/.vscode/launch.json b/apps/api/.vscode/launch.json new file mode 100644 index 0000000..0585794 --- /dev/null +++ b/apps/api/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: FastAPI", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": ["src.main:app", "--reload"], + "jinja": true + } + ] +} diff --git a/apps/api/.vscode/settings.json b/apps/api/.vscode/settings.json new file mode 100644 index 0000000..d969f96 --- /dev/null +++ b/apps/api/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.testing.pytestArgs": ["tests"], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 0000000..51da84c --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,8 @@ +Generate requirements.txt using Poetry package manager: + +``` +poetry export --without-hashes --format=requirements.txt > requirements.txt +``` + +Python monorepo info: +https://medium.com/@ashley.e.shultz/python-mono-repo-with-only-built-in-tooling-7c2d52c2fc66 diff --git a/apps/api/harry-potter-db-seed-spells.csv b/apps/api/harry-potter-db-seed-spells.csv new file mode 100644 index 0000000..32bcf5f --- /dev/null +++ b/apps/api/harry-potter-db-seed-spells.csv @@ -0,0 +1,78 @@ +id,name,description +c76a2922-ba4c-4278-baab-44defb631236,Aberto,Opens locked doors +06485500-d023-4799-93fd-77f2c3341aa3,Accio,Summons objects +acbc0ae1-12e1-4813-b51e-09d22de40475,Aguamenti,Summons water +c9d2f389-a419-4f7e-8d3d-254959638019,Alohomora,Unlocks objects +018429a5-15d5-41af-bf8f-98a966733d77,Anapneo,Clears someone's airway +c828685c-52d2-466d-bcc6-fbcd8376cfb5,Aparecium,Reveals secret written messages +7fdd393c-2608-4ef3-9fd0-f691ad6f8b88,Apparate,A non-verbal transportation spell that allows a witch or wizard to instantly travel on the spot and appear at another location (disapparate is the opposite) +73886d47-2808-4861-ae40-956f4cb56272,Ascendio,Propells someone into the air +9a6b6854-8858-4b21-b761-12526a154597,Avada Kedavra,"Also known as The Killing Curse, the most evil spell in the Wizarding World; one of three Unforgivable Curses; Harry Potter is the only known witch or wizard to survive it" +b6f20bba-c0db-4ad2-8ac6-2a375a596287,Avis,Conjures a small flock of birds +48edfe4d-ddfc-49ac-8065-bd7e73c73778,Bat,Bogey Hex - Turns the target's boogers into bats +6bd8d5c1-9375-4b70-8d6e-ad019176c7a2,Bombardo,Creates an explosion +8fc19d10-3130-4b85-95c1-f2a51ba5ee3c,Brackium Emendo,Heals broken bones +f08c17fa-7bf9-49bf-9fba-a7806815bc80,Capacious Extremis,"Known as the Extension Charm, it's a complicated spell that can greatly expand or extend the capacity of an object or space without affecting it externally" +55dec867-ac07-4975-94c7-090f6fd25c86,Confundo,"Known as the Confundus Charm, it causes confusion of the target" +816d9fee-b78f-47b4-be46-b48626c013f9,Conjunctivitis Curse,Affects the eyes and sight of a target +58b8727a-6c0c-469c-b91d-8ac0cc0dd2d8,Crinus Muto,Changes hair color and style +f1e91049-e866-4f6f-9d87-d6fd366aecbf,Crucio,"One of three Unforgivable Curses, it causes unbearable pain in the target" +7324e645-8f41-4c83-a367-0d10a72906ff,Diffindo,Used to precisely cut an object +638072b9-b7ac-405d-914b-d9293c5f9d25,Disillusionment Charm,Causes the target to take on the appearance of its surroundings +b7643e32-ef9c-41b8-83f2-03f6b5015e04,Disapparate,A non-verbal transportation spell that allows a witch or wizard to instantly travel on the spot and leave for another location (apparate is the opposite) +20476c31-4f27-49ac-876f-a4c4028f1b5b,Engorgio,Causes rapid growth in the targeted object +ecb9a882-d6d7-495e-9958-a1a06902bb65,Episkey,Heals minor injuries +317ff981-ad65-421e-92fb-5f6647d95232,Expecto patronum,"The Patronus Charm is a powerful projection of hope and happiness that drives away Dementors; a corpeal Patronus takes the the respective animal form of the caster, while a non-corpeal appears as a wisp of light; at 13, Harry Potter was the youngest known witch or wizard to prouduce a corpeal Patronus" +60149246-91cf-44a5-8885-78a7acc4bf90,Erecto,"Allows a witch or wizard to build a structure, like a tent" +6d8138c3-0773-4c23-b0bf-aab0e5c6fd95,Evanesco,Vanishes objects +678474e6-fb30-4bf0-a18c-228f6b36592d,Expelliarmus,Forces an opponent to drop whatever's in their possession +31b38b6c-4775-4e20-815d-dbf302433de6,Ferula,A healing charm that conjures wraps and bandages for wounds +37d262c9-28ab-408f-9576-acf54ce50203,Fidelius Charm,"A complex charm that conceals a secret into the soul of a chosen ""Secret Keeper"". If a location is the subject of concealment, it becomes undetectable to others" +9121b557-0ebf-4b60-a119-9d1c5ff05dee,Fiendfyre Curse,"Conjures destructive, enormous enchanted flames" +de23025f-5e6a-4ec3-b827-5c526a922a89,Finite Incantatem,A general counter-spell that's used to reverse or counter already cast charms +d536cbe5-bc0f-49e5-b063-e02c231a3988,Furnunculus Curse,A jinx that causes a breakout of boils or pimples +7915b07a-d26e-4057-9083-e457643e57a6,Geminio,Duplicates objects +2a942514-7a19-4f0e-9353-171c573abcba,Glisseo,Transforms a staircase into a slide +a42028b6-67f5-463b-b759-452103533227,Homenum Revelio,Reveals the presence of another person +552cd4ee-2c67-48fd-ae20-a83773262a8a,Homonculus Charm,Detects anyone's true identity and location on a piece of parchment; used to create the Marauder's Map +2dfca7d2-ec9b-4150-b3f3-fd972a5fd1bc,Immobulus,Immobilises living targets +a49300cc-ddbf-4ff4-b8c2-e8bddbbe4118,Impedimenta,A temporary jinx that slows the movement of the target +e5c22d31-26f1-4c88-a586-d9c09cb88c1f,Incarcerous,Conjures ropes +a53ad5be-00ee-4254-b3c0-4cec60b0c034,Imperio,"One of the three Unforgivable Curses, it places the target under the complete control of the caster" +a3b34bf6-1ff7-4fe3-81ee-e617150f5da9,Impervius,Makes an object waterproof +de048df0-b227-4376-a29b-90fe6878d950,Incendio,Conjures flames +c4a4520b-b80d-49e8-9e5a-3ca0a7f376ca,Langlock,Causes the target's tongue to stick to the roof of their mouth +0da7cb76-dabc-46ff-b8e9-c23a4f03caea,Legilimens,Invading or navigating another's mind +723dd9c9-ee62-495b-9071-cddd16087b86,Levicorpus,Levitates the target by their ankle +8add16ef-b4b1-4e2b-a91e-80aa194da438,Locomotor Mortis,The Leg-Locker curse bounds the target's legs +3b7a10ce-3339-4a36-9493-292c8775e47b,Lumos,Illuminates the caster's wand +832edaca-dbff-4a57-80c7-1d8a827c8416,Morsmordre,Conjures and projects Lord Voldemort's Dark Mark +7f4b43e0-3356-43f9-9299-15ec37cfaf76,Mucus Ad Nauseam,Inflicts an extreme runny nose or cold +f86bbf7e-94ea-4c22-89fb-809af8214a85,Muffliato,Creates a buzzing sound in the target's ears to prevent eavesdropping +66be613d-532c-46d8-a3e9-f5a2d9cccf0c,Nox,"Reverses the lumos charm, extinguishing a wand's light" +9e3c0217-652a-4763-82f8-5519026a1ea6,Obliviate,Erases the target's memory +0af49753-c8ae-4748-87a7-b7cfc47d33a0,Obscuro,Conjures a blindfold +67e838c1-4623-414e-9a91-12125631dbad,Oculus Reparo,Repairs eyeglasses +12251f32-af9d-408f-a652-3a4cc9602bc0,Oppugno,Directs an object or person to attack a victim +da9eab7b-2c7c-42de-861c-fb254bd9423c,Petrificus Totalus,Temporarily freezes or petrifies the body of the target +3e5fd245-2ecf-40c4-937d-b2c2f9eee003,Periculum,Conjures flares/red sparks +ad5685f8-6e05-49b1-a41c-d72786001d72,Piertotum Locomotor,Incantation used to bring to life inanimate objects and artifacts +0a267162-0594-4372-a3d5-89382926f495,Protean Charm,Links objects together for better communication +8808aa30-39f4-400c-a0e5-1dcbad657931,Protego,"Casts an invisible shield around the caster, protecting against spells and objects (except for The Killing Curse)" +56742dd7-3c93-4085-bea3-971e88d81dc2,Reducto,Reduces the target to pieces +2f177949-1f80-4663-9840-da8197411f2a,Reducio,Shrinks an enlarged object to its regular size +358ecb3c-e684-492c-b706-47cbd1eae02e,Renneverate,Awakens or revives the target +1b7a8a4c-8d4f-4001-8155-e68f1198ef72,Reparifors,Heals magical ailments like poisoning or paralysis +799f31a3-799e-411f-b67c-a64e48a5f503,Reparo,Fixes broken objects +32dbeb89-0978-4037-ab1b-413d62be02c3,Rictusempra,A charm that disarms an opponent by tickling them +c9dc8bed-5834-4001-8fa1-852690d027f2,Riddikulus,"Used to defeat a Boggart, the charm allows the scary creature to assume a comedic form, disarming it" +14c47e18-cbf3-4aec-afd3-5473d18ee7c0,Scourgify,Cleans objects +3617c34c-e650-4e3b-a13a-651d18471225,Sectumsempra,Inflicts severe lacerations and haemorrhaging on the target +53747fb8-bdab-466e-90fb-ca75c66f3dd9,Serpensortia,Conjures a live snake +43d3d53e-7ab9-4145-bda7-d96be99c5d31,Silencio,Silences the target +d5f71164-fa43-4566-b537-8852859bde01,Sonorus,Amplifies the witch or wizard's voice +9ec3258c-bc2f-4427-8440-ebea450a44aa,Spongify,Softens the target +37110a48-07e3-4fd7-9aae-ac1145161e1e,Stupefy,The Stunning spell freezes objects and renders living targets unconscious +daeb6f2a-5aff-43e1-964a-a06da7f66a3c,Tarantallegra,"Aimed at the legs, causes uncontrollable dancing movement" +4eaa6532-3ef2-428d-922f-101aee3d66ed,Unbreakable Vow,A magically binding contract that results in the death of whoever breaks it +e23728b2-f6fd-4c70-a1d2-ce602940d873,Wingardium Leviosa,"Causes an object to levitate; but remember what Hermione said: ""It's Wing-gar-dium Levi-o-sa, make the 'gar' nice and long.'""" diff --git a/apps/api/harry-potter-db-seed-users.csv b/apps/api/harry-potter-db-seed-users.csv new file mode 100644 index 0000000..49aa6a1 --- /dev/null +++ b/apps/api/harry-potter-db-seed-users.csv @@ -0,0 +1,114 @@ +id,forename,surname,email +fd142190-f1d7-4ce2-bdb3-6ed6b3edc020,Patricia,Stimpson,patriciastimpson@hogwarts.com +f94086b8-03ae-4457-ba2c-e624d0980869,Lavender,Brown,lavenderbrown@hogwarts.com +ecca342d-d345-4fb3-8a85-ece848ab8938,Milicent,Bullstroude,milicentbullstroude@hogwarts.com +ec714982-e604-40d4-bd4c-dc5155506957,Morag,MacDougal,moragmacdougal@hogwarts.com +eaea5eb3-48a3-41c6-9ea5-c695299bab16,Lisa,Turpin,lisaturpin@hogwarts.com +e7f4554e-8193-4b16-a40b-a8b38a0c3e57,Graham,Montague,grahammontague@hogwarts.com +e65c8acb-0dfc-4f15-bcf8-d32b78811093,Rose,Zeller,rosezeller@hogwarts.com +e4653b01-76a5-4769-a6a2-1f2efaf89cbb,Rose,Weasley,roseweasley@hogwarts.com +e32dd37c-91cd-4950-8ef2-e2ba1b87bd75,Lily,Moon,lilymoon@hogwarts.com +dcdc063e-cf3e-48fc-b777-65922e899b38,Albus,Severus,albusseverus@hogwarts.com +d9cec110-a1d0-4437-9a55-dced475dfe6d,Andrew,Kirke,andrewkirke@hogwarts.com +d5c4daa3-c726-426a-aa98-fb40f3fba816,Cedric,Diggory,cedricdiggory@hogwarts.com +cf3707ad-e816-4b54-90d0-403800a06ecd,Emma,Dobbs,emmadobbs@hogwarts.com +cb263aed-289b-43ad-8647-db54b8a5fc92,Michael,Corner,michaelcorner@hogwarts.com +c8aed011-ab8f-46df-9e8d-dde938256ea9,Miles,Bletchley,milesbletchley@hogwarts.com +c74b1fae-4793-4b47-bec2-ee652beabce2,Ritchie,Coote,ritchiecoote@hogwarts.com +c61b5c80-2c8e-404f-88ca-349a6344f35c,Cassius,Warrington,cassiuswarrington@hogwarts.com +c5acae3e-1a05-4f1d-bb83-3f8c7639d84e,Mandy,Brocklehurst,mandybrocklehurst@hogwarts.com +c3b1f9a5-b87b-48bf-b00d-95b093ea6390,Ron,Weasley,ronweasley@hogwarts.com +c29cd5f9-d2c3-4be9-ba1c-04169cdf511b,Alicia,Spinet,aliciaspinet@hogwarts.com +bff82738-5bb0-4edc-9cec-f80d1af4801f,Vicky,Frobisher,vickyfrobisher@hogwarts.com +b78e6677-8bb4-4eb7-97cb-2f86677e27ea,Adrian,Pucey,adrianpucey@hogwarts.com +b634f0a1-7b48-49b6-b039-27f947ee76fd,Angelina,Johnson,angelinajohnson@hogwarts.com +b01be346-290b-4f65-9c88-a49922e116ee,Orla,Quirke,orlaquirke@hogwarts.com +af95bd8a-dfae-45bb-bc69-533860d34129,Draco,Malfoy,dracomalfoy@hogwarts.com +ae068570-8419-4063-bf61-ba4a0ef41fe3,Laura,Madley,lauramadley@hogwarts.com +a93b80a0-987d-4148-944d-16043df95e8c,Dennis,Creevey,denniscreevey@hogwarts.com +a506574f-c8cf-46c6-a8ac-2f805c25e49e,Graham,Pritchard,grahampritchard@hogwarts.com +a3e5ea64-b103-4f47-bc26-dc08b799c668,Eddie,Carmichael,eddiecarmichael@hogwarts.com +a31ddc78-af12-4978-929c-3cc8a00a833e,Gregory,Goyle,gregorygoyle@hogwarts.com +a01f6dbb-bad5-426b-a7df-f9613fa1021d,Euan,Abercrombie,euanabercrombie@hogwarts.com +9e3f7ce4-b9a7-4244-b709-dae5c1f1d4a8,Harry,Potter,harrypotter@hogwarts.com +9c8ce8c7-ae0a-4646-920f-09c071862f10,James,Potter,jamespotter@hogwarts.com +9ba0ca6e-4fba-410d-9b5e-e20694dde413,Melinda,Bobbin,melindabobbin@hogwarts.com +9ac09267-92ea-444a-a395-28f3b0f6fe6f,Terrence,Higgs,terrencehiggs@hogwarts.com +98546bab-8d5b-4627-95f6-38e306d58a91,Owen,Cauldwell,owencauldwell@hogwarts.com +979ab773-944f-4ff8-88be-943a4bc2c18a,Lee,Jordan,leejordan@hogwarts.com +938559ee-e8e5-4963-8437-e7da04fd1b31,Marcus,Belby,marcusbelby@hogwarts.com +9055a7b1-6ac9-4363-977c-4dec78572fad,Terry,Boot,terryboot@hogwarts.com +8f9aa40b-5d7c-441e-ad32-4564ecda3b70,Cho,Chang,chochang@hogwarts.com +8f3b8796-c7b9-442e-ac02-113d48306fc7,Percy,Weasley,percyweasley@hogwarts.com +8e557e86-28d5-433f-8ac1-d2cecfeb8fb7,Colin,Creevey,colincreevey@hogwarts.com +88886e27-9ce2-416f-9dd6-56d4cd94a4fb,Geoffrey,Hooper,geoffreyhooper@hogwarts.com +861c4cde-2f0f-4796-8d8f-9492e74b2573,Luna,Lovegood,lunalovegood@hogwarts.com +7f2f6207-8998-4f98-92c2-8d02898a82eb,Scorpius,Malfoy,scorpiusmalfoy@hogwarts.com +7cc5e694-850d-4c44-830b-7154e23bb5c3,Susan,Bones,susanbones@hogwarts.com +781f1061-5413-40c7-8a35-b078e2e969b1,Barnabas,the,barnabasthe@hogwarts.com +7772cb4e-5c33-405a-970d-c05cae167917,Daphne,Greengrass,daphnegreengrass@hogwarts.com +6fa93583-b935-4228-91e0-729e6713bdab,Zacharias,Smith,zachariassmith@hogwarts.com +6c4350a9-2356-4bba-96bd-0458c12d99b5,Romilda,Vane,romildavane@hogwarts.com +69c18f6a-cd97-4218-9f2f-740393e6eb1f,Padma,Patil,padmapatil@hogwarts.com +61b6d68e-4128-408c-9f71-9ef167cb0e69,Anthony,Goldstein,anthonygoldstein@hogwarts.com +58a287c2-8c7a-485a-b095-8c6dcfc7f31d,Lucian,Bole,lucianbole@hogwarts.com +57fe29d4-312a-4711-bd9a-c320253d9176,Victoire,Weasley,victoireweasley@hogwarts.com +575fbbc2-ac94-4c58-92f6-e5d75846da91,Jack,Sloper,jacksloper@hogwarts.com +4eef1e03-cf1c-4441-9119-a6e47a61f880,Kevin,Whitby,kevinwhitby@hogwarts.com +4c7e6819-a91a-45b2-a454-f931e4a7cce3,Hermione,Granger,hermionegranger@hogwarts.com +4a0f4c3b-14dc-4a9e-a2f8-da23734f5d34,Marietta,Edgecombe,mariettaedgecombe@hogwarts.com +48880498-3903-4914-bd11-ec650d803199,Natalie,McDonald,nataliemcdonald@hogwarts.com +47aa7511-59b9-4760-9bd7-822a1103177b,Theodore,Nott,theodorenott@hogwarts.com +458828b3-82a5-4cad-a784-e23215825765,Peregrine,Derrick,peregrinederrick@hogwarts.com +42915280-ba56-4ab8-8b17-9511ba2ab093,Penelope,Clearwater,penelopeclearwater@hogwarts.com +3db6dc51-b461-4fa4-a6e4-b1ff352221c5,Neville,Longbottom,nevillelongbottom@hogwarts.com +3d629315-1dbb-4e1e-840d-ffbf45bd5894,Sally-Anne,Perks,sally-anneperks@hogwarts.com +341e65d4-6917-48d7-80b2-1f9af607e95a,Cormac,McLaggen,cormacmclaggen@hogwarts.com +34155375-c8c0-415e-873a-b6483f0cbf17,Justin,Finch-Fletchley,justinfinch-fletchley@hogwarts.com +2f8db183-e935-4b91-884f-fb9effe42ab8,Malcolm,Baddock,malcolmbaddock@hogwarts.com +2b203c7e-7b3d-4f27-8b3c-11473904da73,Hugo,Weasley,hugoweasley@hogwarts.com +2a0615de-8aa4-41e7-9504-dd875f5f3f01,George,Weasley,georgeweasley@hogwarts.com +29adbbf0-417a-4c97-8467-adb5341f75e5,Eleanor,Branstone,eleanorbranstone@hogwarts.com +28e9fe6b-3ca5-41ca-8a14-b995e0fb398b,Jimmy,Peakes,jimmypeakes@hogwarts.com +2899e63f-ed02-4152-8ace-0270a068a70d,Pansy,Parkinson,pansyparkinson@hogwarts.com +28741184-263c-4000-b011-ca7c60466ef4,Fred,Weasley,fredweasley@hogwarts.com +2832bea8-7aad-4160-a748-442f5770d586,Katie,Bell,katiebell@hogwarts.com +26bd4437-73fa-4865-afdd-2fc1456f4592,Kenneth,Towler,kennethtowler@hogwarts.com +1fab149b-52b1-4ffe-be52-4eda25d98f5d,Blaise,Zabini,blaisezabini@hogwarts.com +1cd6dc64-01a9-4379-9cfd-1a7167ba1bb1,Ginny,Weasley,ginnyweasley@hogwarts.com +14aca981-2b60-413e-8f8e-3534961b534b,Millicent,Bulstrode,millicentbulstrode@hogwarts.com +1413e1b3-2903-4a47-a2d5-e8abc5ce8014,Seamus,Finnigan,seamusfinnigan@hogwarts.com +13a54f8a-7f68-4add-a1b4-49f60c8e7bcc,Eloise,Midgen,eloisemidgen@hogwarts.com +0e53860c-7679-49e4-891e-fb92286f0e5b,Demelza,Robins,demelzarobins@hogwarts.com +0e42ecbe-27b2-4940-9b03-00182a92c415,Marcus,Flint,marcusflint@hogwarts.com +0c80d701-fa23-4126-9711-efe5f3c4789a,Ernie,Macmillan,erniemacmillan@hogwarts.com +0af82694-e24f-45ec-a8d7-5bb1199ce631,Hannah,Abbott,hannahabbott@hogwarts.com +0a13bf8e-a763-44cc-ac76-c6c53a639809,Roger,Davies,rogerdavies@hogwarts.com +09396e81-d317-499f-b330-25b90ba17d20,Oliver,Wood,oliverwood@hogwarts.com +05bd5fd1-f347-45e6-8ec0-59b7f11c2aec,Stewart,Ackerley,stewartackerley@hogwarts.com +04f9eb45-d843-4e29-a7d3-0bd49ed87f85,Vincent,Crabbe,vincentcrabbe@hogwarts.com +0201cf73-8a86-4358-b232-2abaa23f09af,Parvati,Patil,parvatipatil@hogwarts.com +ca3827f0-375a-4891-aaa5-f5e8a5bad225,Minerva,McGonagall,minervamcgonagall@hogwarts.com +3569d265-bd27-44d8-88e8-82fb0a848374,Severus,Snape,severussnape@hogwarts.com +36bfefd0-e0bb-4d11-be98-d1ef6117a77a,Rubeus,Hagrid,rubeushagrid@hogwarts.com +b8f9095b-9de6-4d7d-83e0-4391af8f22e4,Remus,Lupin,remuslupin@hogwarts.com +2fb675cd-5505-4c8e-a54e-579e73bf4174,Horace,Slughorn,horaceslughorn@hogwarts.com +d58e7249-19d1-40bd-a43f-1da0497fe8aa,Dolores,Umbridge,doloresumbridge@hogwarts.com +b0620914-858d-46fc-8e6d-033c565e138b,Mrs,Norris,mrsnorris@hogwarts.com +2b82cfb8-0440-4a57-a030-6d75a40c0d98,Argus,Filch,argusfilch@hogwarts.com +b415c867-1cff-455e-b194-748662ac2cca,Albus,Dumbledore,albusdumbledore@hogwarts.com +e9457467-d10a-4893-afa9-19f9602b218a,Madam,Pomfrey,madampomfrey@hogwarts.com +ba19be27-178b-4594-95b7-51ba0e3ba1dd,Quirinus,Quirrel,quirinusquirrel@hogwarts.com +e8694719-a975-48fb-9523-f4cade1c38aa,Pomona,Sprout,pomonasprout@hogwarts.com +6ea188f3-d95c-407c-ab00-a0bec8678a71,Cuthbert,Binns,cuthbertbinns@hogwarts.com +a61e0783-7914-4f8d-a800-c409c06315cf,Filius,Flitwick,filiusflitwick@hogwarts.com +0a81c4f9-b80d-45a7-a4fd-9191453815a1,Madam,Hooch,madamhooch@hogwarts.com +3d687c4d-852e-470f-bac5-5a02758b1f8f,Gilderoy,Lockhart,gilderoylockhart@hogwarts.com +cdec9b95-c7a5-4623-ad12-6fa76d168588,Madame,Pince,madamepince@hogwarts.com +8ea29415-012d-4781-ba5f-d0de63a05abe,Sybill,Trelawney,sybilltrelawney@hogwarts.com +58f2cf41-392c-4e84-b441-dbbce585f78d,Septima,Vector,septimavector@hogwarts.com +99d3ce6b-6a45-495a-a7c6-132203697d45,Aurora,Sinistra,aurorasinistra@hogwarts.com +41ebe856-f0f4-4c77-8795-4735d3a87f3d,Alastor,Moody,alastormoody@hogwarts.com +b48c5b8a-4066-4c24-ba26-7677f5ed2b6f,Wilhelmina,Grubbly-Plank,wilhelminagrubbly-plank@hogwarts.com +c4e73590-3ee2-4125-87fb-692dd991819b,Galatea,Merrythought,galateamerrythought@hogwarts.com +61d78dce-890b-4f02-844f-b41d66553802,Charity,Burbage,charityburbage@hogwarts.com diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..57fae1e --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,8 @@ +{ + "name": "api", + "version": "0.0.0", + "scripts": { + "dev": ".\\.venv\\Scripts\\python run.py", + "generate:requirements": "poetry export --without-hashes --format=requirements.txt > requirements.txt" + } +} \ No newline at end of file diff --git a/apps/api/poetry.lock b/apps/api/poetry.lock new file mode 100644 index 0000000..d70d202 --- /dev/null +++ b/apps/api/poetry.lock @@ -0,0 +1,1789 @@ +[[package]] +name = "aiohttp" +version = "3.9.3" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["brotlicffi", "brotli", "aiodns"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +category = "main" +optional = false +python-versions = ">=3.8" + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["packaging", "Sphinx (>=7)", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["anyio", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "argcomplete" +version = "3.2.3" +description = "Bash tab completion for argparse" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +test = ["coverage", "pexpect", "wheel", "ruff", "mypy"] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +category = "dev" +optional = false +python-versions = ">=3.8.0" + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +cov = ["attrs", "coverage[toml] (>=5.3)"] +dev = ["attrs", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs", "cloudpickle", "hypothesis", "pympler", "pytest-xdist", "pytest (>=4.3.0)"] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "commitizen" +version = "3.18.4" +description = "Python commitizen client tool" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +argcomplete = ">=1.12.1,<3.3" +charset-normalizer = ">=2.1.0,<4" +colorama = ">=0.4.1,<0.5.0" +decli = ">=0.6.0,<0.7.0" +importlib_metadata = ">=4.13,<8" +jinja2 = ">=2.10.3" +packaging = ">=19" +pyyaml = ">=3.08" +questionary = ">=2.0,<3.0" +termcolor = ">=1.1,<3" +tomlkit = ">=0.5.3,<1.0.0" + +[[package]] +name = "decli" +version = "0.6.1" +description = "Minimal, easy-to-use, declarative cli tool" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "deprecation" +version = "2.1.0" +description = "A library to handle automated deprecations" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +packaging = "*" + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest-cov (>=4.1.0)", "pytest (>=7.4)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.109.2" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.36.3,<0.37.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.8" + +[[package]] +name = "gotrue" +version = "2.4.1" +description = "Python Client Library for GoTrue" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +httpx = ">=0.23,<0.26" +pydantic = ">=1.10,<3" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "httpcore" +version = "1.0.4" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] +trio = ["trio (>=0.22.0,<0.25.0)"] + +[[package]] +name = "httpx" +version = "0.25.2" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = ">=1.0.0,<2.0.0" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "7.0.2" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ruff (>=0.2.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.8.0" + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.8" + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx (>=7.2.6)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest (>=7.4.3)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "postgrest" +version = "0.16.1" +description = "PostgREST client for Python. This library provides an ORM interface to PostgREST." +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +deprecation = ">=2.1.0,<3.0.0" +httpx = ">=0.24,<0.26" +pydantic = ">=1.9,<3.0" +strenum = ">=0.4.9,<0.5.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.36" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "pydantic" +version = "2.6.4" +description = "Data validation using Python type hints" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.16.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.3" +description = "" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.2.1" +description = "Settings management using Pydantic" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +category = "dev" +optional = false +python-versions = ">=3.8.0" + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +pylint = ">=1.7" + +[[package]] +name = "pylint-pydantic" +version = "0.3.2" +description = "A Pylint plugin to help Pylint understand the Pydantic" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +pydantic = "<3.0" +pylint = ">2.0,<4.0" +pylint-plugin-utils = "*" + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "questionary" +version = "2.0.1" +description = "Python library to build pretty command line user prompts ⭐️" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +prompt_toolkit = ">=2.0,<=3.0.36" + +[[package]] +name = "realtime" +version = "1.0.2" +description = "" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +python-dateutil = ">=2.8.1,<3.0.0" +typing-extensions = ">=4.2.0,<5.0.0" +websockets = ">=11.0,<12.0" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "starlette" +version = "0.36.3" +description = "The little ASGI library that shines." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "storage3" +version = "0.7.3" +description = "Supabase Storage client for Python." +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +httpx = ">=0.24,<0.26" +python-dateutil = ">=2.8.2,<3.0.0" +typing-extensions = ">=4.2.0,<5.0.0" + +[[package]] +name = "strenum" +version = "0.4.15" +description = "An Enum that inherits from str." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme", "myst-parser"] +release = ["twine"] +test = ["pytest", "pytest-black", "pytest-cov", "pytest-pylint", "pylint"] + +[[package]] +name = "supabase-py-async" +version = "2.5.5" +description = "supabase-py with async synax" +category = "main" +optional = false +python-versions = ">=3.9,<4.0" + +[package.dependencies] +aiohttp = ">=3.9.0,<4.0.0" +commitizen = ">=3.13.0,<4.0.0" +deprecation = ">=2.1.0,<3.0.0" +gotrue = ">=1.2,<3.0" +postgrest = ">=0.13,<0.17" +realtime = ">=1.0.2,<2.0.0" +storage3 = ">=0.6.1,<0.8.0" +supafunc = "0.4.0" + +[[package]] +name = "supafunc" +version = "0.4.0" +description = "Library for Supabase Functions" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +httpx = ">=0.24,<0.26" + +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +category = "main" +optional = false +python-versions = ">=3.8" + +[[package]] +name = "uvicorn" +version = "0.27.1" +description = "The lightning-fast ASGI server." +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ruff (>=0.2.1)", "jaraco.itertools", "jaraco.functools", "more-itertools", "big-o", "pytest-ignore-flaky", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.9" +content-hash = "534906ed9fdc495849ec95e0f62d963b5b8342bb248fa46e4675ed6b31805230" + +[metadata.files] +aiohttp = [ + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, +] +aiosignal = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] +annotated-types = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] +anyio = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] +argcomplete = [ + {file = "argcomplete-3.2.3-py3-none-any.whl", hash = "sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c"}, + {file = "argcomplete-3.2.3.tar.gz", hash = "sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23"}, +] +astroid = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] +async-timeout = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] +attrs = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] +black = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] +certifi = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] +charset-normalizer = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] +click = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] +colorama = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +commitizen = [ + {file = "commitizen-3.18.4-py3-none-any.whl", hash = "sha256:42c9b2c5fd3d6b83ebf850424227a8935d3e49f9fa636c58c072a370713b176a"}, + {file = "commitizen-3.18.4.tar.gz", hash = "sha256:57b3051d4170e23a5317f348d1bc61b98e57ac01b04f66e0f9a25fef75e6f679"}, +] +decli = [ + {file = "decli-0.6.1-py3-none-any.whl", hash = "sha256:7815ac58617764e1a200d7cadac6315fcaacc24d727d182f9878dd6378ccf869"}, + {file = "decli-0.6.1.tar.gz", hash = "sha256:ed88ccb947701e8e5509b7945fda56e150e2ac74a69f25d47ac85ef30ab0c0f0"}, +] +deprecation = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] +dill = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] +dnspython = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] +email-validator = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] +exceptiongroup = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] +fastapi = [ + {file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"}, + {file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"}, +] +frozenlist = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] +gotrue = [ + {file = "gotrue-2.4.1-py3-none-any.whl", hash = "sha256:9647bb7a585c969d26667df21168fa20b18f91c5d6afe286af08d7a0610fd2cc"}, + {file = "gotrue-2.4.1.tar.gz", hash = "sha256:8b260ef285f45a3a2f9b5a006f12afb9fad7a36a28fa277f19e733f22eb88584"}, +] +h11 = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] +httpcore = [ + {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"}, + {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"}, +] +httpx = [ + {file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"}, + {file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"}, +] +idna = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] +importlib-metadata = [ + {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, + {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, +] +iniconfig = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] +isort = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] +jinja2 = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] +mccabe = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] +multidict = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] +mypy-extensions = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] +packaging = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] +pathspec = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] +platformdirs = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] +pluggy = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] +postgrest = [ + {file = "postgrest-0.16.1-py3-none-any.whl", hash = "sha256:412ec6bf61c58f38c92b6b61f57ab50e25c73ca9ef415a6f56ed9cf5429614cb"}, + {file = "postgrest-0.16.1.tar.gz", hash = "sha256:d955824d37e7123a8313cbf10c8e0a8d42418fcb942cd8e1526e8509fb71574d"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, +] +pydantic = [ + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, +] +pydantic-core = [ + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, +] +pydantic-settings = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] +pylint = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] +pylint-plugin-utils = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] +pylint-pydantic = [ + {file = "pylint_pydantic-0.3.2-py3-none-any.whl", hash = "sha256:e5cec02370aa68ac8eff138e5d573b0ac049bab864e9a6c3a9057cf043440aa1"}, +] +pytest = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] +python-dateutil = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] +python-dotenv = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] +pyyaml = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] +questionary = [ + {file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"}, + {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"}, +] +realtime = [ + {file = "realtime-1.0.2-py3-none-any.whl", hash = "sha256:8f8375199fd917cd0ded818702321f91b208ab72794ade0a33cee9d55ae30f11"}, + {file = "realtime-1.0.2.tar.gz", hash = "sha256:776170a4329edc869b91e104c554cda02c8bf8e052cbb93c377e22482870959c"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +sniffio = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] +starlette = [ + {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, + {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, +] +storage3 = [ + {file = "storage3-0.7.3-py3-none-any.whl", hash = "sha256:dc6a59da801ee6fc00015da4967ac0b5c3e5508d31ffd796f0e4c83957e5c6a0"}, + {file = "storage3-0.7.3.tar.gz", hash = "sha256:943c31de4a7c7490ad7960d963a6b410979ebd0e1b3d320d76cb61564ab0b528"}, +] +strenum = [ + {file = "StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659"}, + {file = "StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff"}, +] +supabase-py-async = [ + {file = "supabase_py_async-2.5.5-py3-none-any.whl", hash = "sha256:cbb7fb870be5bfa5df73b8191c2f41134bc65157e7f4bee646ccc7332344a4a3"}, + {file = "supabase_py_async-2.5.5.tar.gz", hash = "sha256:77a1e0474763c7509e6b5c77259ddbdd209faa863348ef4075959a109e32860f"}, +] +supafunc = [ + {file = "supafunc-0.4.0-py3-none-any.whl", hash = "sha256:260c833523c3f5430a4f49ededd9b09a79ddcfe41279be5f987ab604731d62a3"}, + {file = "supafunc-0.4.0.tar.gz", hash = "sha256:7fc3b1eca86f6aacf65030fd74b0c8b9e6bbf07e690319e7570a9510a917c755"}, +] +termcolor = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tomlkit = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] +typing-extensions = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] +uvicorn = [ + {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, + {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, +] +wcwidth = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] +websockets = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] +yarl = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] +zipp = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] diff --git a/apps/api/poetry.toml b/apps/api/poetry.toml new file mode 100644 index 0000000..62e2dff --- /dev/null +++ b/apps/api/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +create = true diff --git a/apps/api/pyproject.toml b/apps/api/pyproject.toml new file mode 100644 index 0000000..b01f7b1 --- /dev/null +++ b/apps/api/pyproject.toml @@ -0,0 +1,24 @@ +[tool.poetry] +name = "api" +version = "0.1.0" +description = "" +authors = ["cording12 "] + +[tool.poetry.dependencies] +python = "^3.9" +fastapi = "^0.109.2" +uvicorn = "^0.27.1" +email-validator = "^2.1.0" +pydantic-settings = "^2.2.1" +python-dotenv = "^1.0.1" +supabase-py-async = "^2.5.5" + +[tool.poetry.dev-dependencies] +isort = "^5.10.1" +black = "^22.6.0" +pytest = "^8.0.1" +pylint-pydantic = "^0.3.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/apps/api/requirements.txt b/apps/api/requirements.txt new file mode 100644 index 0000000..b366c59 --- /dev/null +++ b/apps/api/requirements.txt @@ -0,0 +1,54 @@ +aiohttp==3.9.3; python_version >= "3.9" and python_version < "4.0" +aiosignal==1.3.1; python_version >= "3.9" and python_version < "4.0" +annotated-types==0.6.0; python_version >= "3.8" +anyio==4.3.0; python_version >= "3.9" and python_version < "4.0" +argcomplete==3.2.3; python_version >= "3.9" and python_version < "4.0" +async-timeout==4.0.3; python_version >= "3.9" and python_version < "3.11" +attrs==23.2.0; python_version >= "3.9" and python_version < "4.0" +certifi==2024.2.2; python_version >= "3.9" and python_version < "4.0" +charset-normalizer==3.3.2; python_version >= "3.9" and python_version < "4.0" and python_full_version >= "3.7.0" +click==8.1.7; python_version >= "3.8" +colorama==0.4.6; python_version >= "3.9" and python_full_version < "3.0.0" and platform_system == "Windows" and python_version < "4.0" or platform_system == "Windows" and python_version >= "3.9" and python_full_version >= "3.7.0" and python_version < "4.0" +commitizen==3.18.4; python_version >= "3.9" and python_version < "4.0" +decli==0.6.1; python_version >= "3.9" and python_version < "4.0" +deprecation==2.1.0; python_version >= "3.9" and python_version < "4.0" +dnspython==2.6.1; python_version >= "3.8" +email-validator==2.1.1; python_version >= "3.8" +exceptiongroup==1.2.0; python_version < "3.11" and python_version >= "3.8" +fastapi==0.109.2; python_version >= "3.8" +frozenlist==1.4.1; python_version >= "3.9" and python_version < "4.0" +gotrue==2.4.1; python_version >= "3.9" and python_version < "4.0" +h11==0.14.0; python_version >= "3.9" and python_version < "4.0" +httpcore==1.0.4; python_version >= "3.9" and python_version < "4.0" +httpx==0.25.2; python_version >= "3.9" and python_version < "4.0" +idna==3.6; python_version >= "3.9" and python_version < "4.0" +importlib-metadata==7.0.2; python_version >= "3.9" and python_version < "4.0" +jinja2==3.1.3; python_version >= "3.9" and python_version < "4.0" +markupsafe==2.1.5; python_version >= "3.9" and python_version < "4.0" +multidict==6.0.5; python_version >= "3.9" and python_version < "4.0" +packaging==24.0; python_version >= "3.9" and python_version < "4.0" +postgrest==0.16.1; python_version >= "3.9" and python_version < "4.0" +prompt-toolkit==3.0.36; python_version >= "3.9" and python_version < "4.0" and python_full_version >= "3.6.2" +pydantic-core==2.16.3; python_version >= "3.8" +pydantic-settings==2.2.1; python_version >= "3.8" +pydantic==2.6.4; python_version >= "3.9" and python_version < "4.0" +python-dateutil==2.9.0.post0; python_version >= "3.9" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.9" and python_version < "4.0" and python_full_version >= "3.3.0" +python-dotenv==1.0.1; python_version >= "3.8" +pyyaml==6.0.1; python_version >= "3.9" and python_version < "4.0" +questionary==2.0.1; python_version >= "3.9" and python_version < "4.0" +realtime==1.0.2; python_version >= "3.9" and python_version < "4.0" +six==1.16.0; python_version >= "3.9" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.9" and python_version < "4.0" and python_full_version >= "3.3.0" +sniffio==1.3.1; python_version >= "3.9" and python_version < "4.0" +starlette==0.36.3; python_version >= "3.8" +storage3==0.7.3; python_version >= "3.9" and python_version < "4.0" +strenum==0.4.15; python_version >= "3.9" and python_version < "4.0" +supabase-py-async==2.5.5; python_version >= "3.9" and python_version < "4.0" +supafunc==0.4.0; python_version >= "3.9" and python_version < "4.0" +termcolor==2.4.0; python_version >= "3.9" and python_version < "4.0" +tomlkit==0.12.4; python_version >= "3.9" and python_version < "4.0" +typing-extensions==4.10.0; python_version < "3.10" and python_version >= "3.9" +uvicorn==0.27.1; python_version >= "3.8" +wcwidth==0.2.13; python_version >= "3.9" and python_version < "4.0" and python_full_version >= "3.6.2" +websockets==11.0.3; python_version >= "3.9" and python_version < "4.0" +yarl==1.9.4; python_version >= "3.9" and python_version < "4.0" +zipp==3.18.1; python_version >= "3.9" and python_version < "4.0" diff --git a/apps/api/run.py b/apps/api/run.py new file mode 100644 index 0000000..a97e37b --- /dev/null +++ b/apps/api/run.py @@ -0,0 +1,4 @@ +import uvicorn + +if __name__ == "__main__": + uvicorn.run("src.main:app", reload=True) diff --git a/apps/api/src/__init__.py b/apps/api/src/__init__.py new file mode 100644 index 0000000..3dc1f76 --- /dev/null +++ b/apps/api/src/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/apps/api/src/api/__init__.py b/apps/api/src/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/src/api/api_v1/__init__.py b/apps/api/src/api/api_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/src/api/api_v1/api.py b/apps/api/src/api/api_v1/api.py new file mode 100644 index 0000000..b4e2f8e --- /dev/null +++ b/apps/api/src/api/api_v1/api.py @@ -0,0 +1,6 @@ +from fastapi import APIRouter +from src.api.api_v1.endpoints import users, spells + +api_router = APIRouter() +api_router.include_router(users.router, prefix="/users", tags=["users"], responses={404: {"description": "Not found"}}) +api_router.include_router(spells.router, prefix="/spells", tags=["spells"], responses={404: {"description": "Not found"}}) diff --git a/apps/api/src/api/api_v1/endpoints/__init__.py b/apps/api/src/api/api_v1/endpoints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/src/api/api_v1/endpoints/spells.py b/apps/api/src/api/api_v1/endpoints/spells.py new file mode 100644 index 0000000..c67d211 --- /dev/null +++ b/apps/api/src/api/api_v1/endpoints/spells.py @@ -0,0 +1,63 @@ +from typing import Literal, Optional, Union + +from fastapi import APIRouter, HTTPException + +from src.api.deps import SessionDep +from src.crud import spell +from src.schemas import Spell, SpellSearchResults + +router = APIRouter() + + +@router.get("/get/", status_code=200, response_model=Spell) +async def get_spell(session: SessionDep, spell_id: str) -> Spell: + """Returns a spell from a spell_id. + + **Returns:** + - spell: spell object. + """ + return await spell.get(session, id=spell_id) + + +@router.get("/get-all/", status_code=200, response_model=list[Spell]) +async def get_all_spells(session: SessionDep) -> list[Spell]: + """Returns a list of all spells. + + **Returns:** + - list[spell]: List of all spells. + """ + return await spell.get_all(session) + + +@router.get("/search/", status_code=200, response_model=SpellSearchResults) +async def search_spells( + session: SessionDep, + search_on: Literal["id", "name", "description"] = "name", + keyword: Optional[Union[str, int]] = None, + max_results: Optional[int] = 10, +) -> SpellSearchResults: + """ + Search for spells based on a keyword and return the top `max_results` items. + + **Args:** + - search_on (str, optional): The field to perform the search on. Defaults to "name". + - keyword (str, optional): The keyword to search for. Defaults to None. + - max_results (int, optional): The maximum number of search results to return. Defaults to 10. + + **Returns:** + - SpellSearchResults: Object containing a list of the top `max_results` items that match the keyword. + """ + if not keyword: + results = await spell.get_all(session) + return SpellSearchResults(results=results) + + results = await spell.search_all( + session, field=search_on, search_value=keyword, max_results=max_results + ) + + if not results: + raise HTTPException( + status_code=404, detail="No spells found matching the search criteria" + ) + + return SpellSearchResults(results=results) diff --git a/apps/api/src/api/api_v1/endpoints/users.py b/apps/api/src/api/api_v1/endpoints/users.py new file mode 100644 index 0000000..a1d4543 --- /dev/null +++ b/apps/api/src/api/api_v1/endpoints/users.py @@ -0,0 +1,76 @@ +from typing import Literal, Optional, Union + +from fastapi import APIRouter, HTTPException + +from src.api.deps import SessionDep +from src.crud import user +from src.schemas import User, UserCreate, UserSearchResults + +router = APIRouter() + + +@router.get("/get/", status_code=200, response_model=User) +async def get_user(session: SessionDep, user_id: str) -> User: + """Returns a user from a user_id. + + **Returns:** + - User: User object. + """ + return await user.get(session, id=user_id) + + +@router.get("/get-all/", status_code=200, response_model=list[User]) +async def get_all_users(session: SessionDep) -> list[User]: + """Returns a list of all users. + + **Returns:** + - list[User]: List of all users. + """ + return await user.get_all(session) + + +@router.get("/search/", status_code=200, response_model=UserSearchResults) +async def search_users( + session: SessionDep, + search_on: Literal["id", "email", "forename", "surname"] = "email", + keyword: Optional[Union[str, int]] = None, + max_results: Optional[int] = 10, +) -> UserSearchResults: + """ + Search for users based on a keyword and return the top `max_results` items. + + **Args:** + - keyword (str, optional): The keyword to search for. Defaults to None. + - max_results (int, optional): The maximum number of search results to return. Defaults to 10. + - search_on (str, optional): The field to perform the search on. Defaults to "email". + + **Returns:** + - UserSearchResults: Object containing a list of the top `max_results` items that match the keyword. + """ + if not keyword: + results = await user.get_all(session) + return UserSearchResults(results=results) + + results = await user.search_all( + session, field=search_on, search_value=keyword, max_results=max_results + ) + + if not results: + raise HTTPException( + status_code=404, detail="No users found matching the search criteria" + ) + + return UserSearchResults(results=results) + + +@router.post("/create", status_code=201, response_model=User) +async def create_user(user_in: UserCreate, session: SessionDep) -> User: + """Craete a new user. + + **Args:** + - user_in (UserCreate): JSON of the user to create. Forename, surname and email. Email must be unique. + + **Returns:** + - User: User object + """ + return await user.create(session, obj_in=user_in) diff --git a/apps/api/src/api/deps.py b/apps/api/src/api/deps.py new file mode 100644 index 0000000..4b227ce --- /dev/null +++ b/apps/api/src/api/deps.py @@ -0,0 +1,30 @@ +from typing import Annotated + +from fastapi import Depends, HTTPException +from supabase_py_async import AsyncClient, create_client +from supabase_py_async.lib.client_options import ClientOptions + +from src.config import settings + + +async def get_db() -> AsyncClient: + client: AsyncClient | None = None + try: + client = await create_client( + settings.DB_URL, + settings.DB_API_KEY, + options=ClientOptions( + postgrest_client_timeout=10, storage_client_timeout=10 + ), + ) + # client = await client.auth.sign_in_with_password( + # {"email": settings.DB_EMAIL, "password": settings.DB_PASSWORD} + # ) + yield client + + except Exception as e: + print(e) + raise + + +SessionDep = Annotated[AsyncClient, Depends(get_db)] diff --git a/apps/api/src/config.py b/apps/api/src/config.py new file mode 100644 index 0000000..f3db238 --- /dev/null +++ b/apps/api/src/config.py @@ -0,0 +1,30 @@ +import os + +from dotenv import load_dotenv +from pydantic_settings import BaseSettings, SettingsConfigDict + +load_dotenv() + +# If the environment is Gitpod, the root path will be the workspace cluster host +# If not using gitpod, you can delete this if statement, but keep the else clause +if os.getenv("USER") == "gitpod": + ROOT_PATH = f"https://8000-cording12-nextfastturbo-qqfo0frc496.{os.getenv('GITPOD_WORKSPACE_CLUSTER_HOST')}" +else: + # Otherwise, the root path will be the local host. ROOT_PATH is an env var configured in Vercel deployment. + # The value for production is equal to the root path of the deployment URL in Vercel. + ROOT_PATH = os.getenv("ROOT_PATH", "http://127.0.0.1:8000") + + +class Settings(BaseSettings): + PROJECT_NAME: str = "FastAPI App" + PROJECT_DESCRIPTION: str = "A simple FastAPI app" + DB_URL: str = os.getenv("DB_URL") + DB_API_KEY: str = os.getenv("DB_API_KEY") + DB_EMAIL: str = os.getenv("DB_EMAIL") + DB_PASSWORD: str = os.getenv("DB_PASSWORD") + model_config = SettingsConfigDict(env_file=".env") + API_VERSION: str = "/api/v1" + ROOT: str = ROOT_PATH + + +settings = Settings() diff --git a/apps/api/src/crud/__init__.py b/apps/api/src/crud/__init__.py new file mode 100644 index 0000000..a2466d9 --- /dev/null +++ b/apps/api/src/crud/__init__.py @@ -0,0 +1,2 @@ +from .crud_spell import spell +from .crud_user import user diff --git a/apps/api/src/crud/base.py b/apps/api/src/crud/base.py new file mode 100644 index 0000000..115d2c8 --- /dev/null +++ b/apps/api/src/crud/base.py @@ -0,0 +1,74 @@ +from typing import Generic, Optional, TypeVar + +from supabase_py_async import AsyncClient + +from src.schemas.base import CreateBase, ResponseBase, UpdateBase + +ModelType = TypeVar("ModelType", bound=ResponseBase) +CreateSchemaType = TypeVar("CreateSchemaType", bound=CreateBase) +UpdateSchemaType = TypeVar("UpdateSchemaType", bound=UpdateBase) + + +class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): + def __init__(self, model: type[ModelType]): + """CRUD object with default methods to do CRUD ops + + Args: + model (type[ModelType]): Model class type + """ + self.model = model + + async def get(self, db: AsyncClient, *, id: str) -> Optional[ModelType]: + """get by table_name by id""" + data, count = ( + await db.table(self.model.table_name).select("*").eq("id", id).execute() + ) + _, got = data + return self.model(**got[0]) if got else None + + async def get_all(self, db: AsyncClient) -> list[ModelType]: + """get all by table_name""" + data, count = await db.table(self.model.table_name).select("*").execute() + _, got = data + return [self.model(**item) for item in got] + + async def search_all( + self, db: AsyncClient, *, field: str, search_value: str, max_results: int + ) -> list[ModelType]: + """search all by table_name""" + data, count = ( + await db.table(self.model.table_name) + .select("*") + .ilike(field, f"%{search_value}%") + .limit(max_results) + .execute() + ) + _, got = data + return [self.model(**item) for item in got] + + async def create(self, db: AsyncClient, *, obj_in: CreateSchemaType) -> ModelType: + """create by CreateSchemaType""" + data, count = ( + await db.table(self.model.table_name).insert(obj_in.model_dump()).execute() + ) + _, created = data + return self.model(**created[0]) + + async def update(self, db: AsyncClient, *, obj_in: UpdateSchemaType) -> ModelType: + """update by UpdateSchemaType""" + data, count = ( + await db.table(self.model.table_name) + .update(obj_in.model_dump()) + .eq("id", obj_in.id) + .execute() + ) + _, updated = data + return self.model(**updated[0]) + + async def delete(self, db: AsyncClient, *, id: str) -> ModelType: + """remove by UpdateSchemaType""" + data, count = ( + await db.table(self.model.table_name).delete().eq("id", id).execute() + ) + _, deleted = data + return self.model(**deleted[0]) diff --git a/apps/api/src/crud/crud_spell.py b/apps/api/src/crud/crud_spell.py new file mode 100644 index 0000000..82bd48c --- /dev/null +++ b/apps/api/src/crud/crud_spell.py @@ -0,0 +1,43 @@ +from typing import Optional + +from fastapi import HTTPException +from supabase_py_async import AsyncClient + +from src.crud.base import CRUDBase +from src.schemas import Spell, SpellCreate, SpellUpdate + + +class CRUDSpell(CRUDBase[Spell, SpellCreate, SpellUpdate]): + async def get(self, db: AsyncClient, *, id: str) -> Optional[Spell]: + try: + return await super().get(db, id=id) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"{e.code}: Spell not found. {e.details}", + ) + + async def get_all(self, db: AsyncClient) -> list[Spell]: + try: + return await super().get_all(db) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while fetching spells. {e}", + ) + + async def search_all( + self, db: AsyncClient, *, field: str, search_value: str, max_results: int + ) -> list[Spell]: + try: + return await super().search_all( + db, field=field, search_value=search_value, max_results=max_results + ) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while searching for users. {e}", + ) + + +spell = CRUDSpell(Spell) diff --git a/apps/api/src/crud/crud_user.py b/apps/api/src/crud/crud_user.py new file mode 100644 index 0000000..a3099ac --- /dev/null +++ b/apps/api/src/crud/crud_user.py @@ -0,0 +1,58 @@ +from typing import Optional + +from fastapi import HTTPException +from supabase_py_async import AsyncClient + +from src.crud.base import CRUDBase +from src.schemas import User, UserCreate, UserUpdate + + +class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): + async def create(self, db: AsyncClient, *, obj_in: UserCreate) -> User: + try: + return await super().create(db, obj_in=obj_in) + except Exception as e: + raise HTTPException( + status_code=400, + detail=f"{e.code}: Failed to create user. {e.details}", + ) + + async def get(self, db: AsyncClient, *, id: str) -> Optional[User]: + try: + return await super().get(db, id=id) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"{e.code}: User not found. {e.details}", + ) + + async def get_all(self, db: AsyncClient) -> list[User]: + try: + return await super().get_all(db) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while fetching users. {e}", + ) + + async def search_all( + self, db: AsyncClient, *, field: str, search_value: str, max_results: int + ) -> list[User]: + try: + return await super().search_all( + db, field=field, search_value=search_value, max_results=max_results + ) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while searching for users. {e}", + ) + + async def update(self, db: AsyncClient, *, obj_in: UserUpdate) -> User: + return await super().update(db, obj_in=obj_in) + + async def delete(self, db: AsyncClient, *, id: str) -> User: + return await super().delete(db, id=id) + + +user = CRUDUser(User) diff --git a/apps/api/src/main.py b/apps/api/src/main.py new file mode 100644 index 0000000..b7dee74 --- /dev/null +++ b/apps/api/src/main.py @@ -0,0 +1,51 @@ +from fastapi import APIRouter, FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.routing import APIRoute + +from src.api.api_v1.api import api_router +from src.config import settings + +info_router = APIRouter() + + +@info_router.get("/", status_code=200, include_in_schema=False) +async def info(): + return [{"Status": "API Running"}] + + +def custom_generate_unique_id(route: APIRoute): + """Generates a custom ID when using the TypeScript Generator Client + + Args: + route (APIRoute): The route to be customised + + Returns: + str: tag-route_name, e.g. items-CreateItem + """ + return f"{route.tags[0]}-{route.name}" + + +def get_application(): + _app = FastAPI( + title=settings.PROJECT_NAME, + description=settings.PROJECT_DESCRIPTION, + generate_unique_id_function=custom_generate_unique_id, + root_path=settings.ROOT, + root_path_in_servers=True, + ) + + _app.include_router(api_router, prefix=settings.API_VERSION) + _app.include_router(info_router, tags=[""]) + + _app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + return _app + + +app = get_application() diff --git a/apps/api/src/schemas/__init__.py b/apps/api/src/schemas/__init__.py new file mode 100644 index 0000000..29f5765 --- /dev/null +++ b/apps/api/src/schemas/__init__.py @@ -0,0 +1,2 @@ +from .spell import Spell, SpellCreate, SpellSearchResults, SpellUpdate +from .user import User, UserCreate, UserSearchResults, UserUpdate diff --git a/apps/api/src/schemas/base.py b/apps/api/src/schemas/base.py new file mode 100644 index 0000000..fd319bb --- /dev/null +++ b/apps/api/src/schemas/base.py @@ -0,0 +1,40 @@ +from typing import ClassVar +from pydantic import BaseModel, ConfigDict + +# Shared properties +# class CRUDBaseModel(BaseModel): +# # where the data +# table_name: str + + +# Properties to receive on item creation +# in +class CreateBase(BaseModel): + # inherent to add more properties for creating + pass + + +# Properties to receive on item update +# in +class UpdateBase(BaseModel): + # inherent to add more properties for updating + id: str + + +# response +# Properties shared by models stored in DB +class InDBBase(BaseModel): + id: str + user_id: str + created_at: str + + +# Properties to return to client +# crud model +# out +class ResponseBase(InDBBase): + # inherent to add more properties for responding + table_name: ClassVar[str] = "ResponseBase".lower() + Config: ClassVar[ConfigDict] = ConfigDict( + extra="ignore", arbitrary_types_allowed=True + ) diff --git a/apps/api/src/schemas/spell.py b/apps/api/src/schemas/spell.py new file mode 100644 index 0000000..38ce46d --- /dev/null +++ b/apps/api/src/schemas/spell.py @@ -0,0 +1,26 @@ +from typing import ClassVar, Sequence + +from pydantic import BaseModel + + +class Spell(BaseModel): + id: str + name: str + description: str + table_name: ClassVar[str] = "spells" + + +class SpellCreate(BaseModel): + id: str + name: str + description: str + + +class SpellUpdate(BaseModel): + id: str + name: str + description: str + + +class SpellSearchResults(BaseModel): + results: Sequence[Spell] diff --git a/apps/api/src/schemas/user.py b/apps/api/src/schemas/user.py new file mode 100644 index 0000000..ad82d9d --- /dev/null +++ b/apps/api/src/schemas/user.py @@ -0,0 +1,31 @@ +from typing import ClassVar, Sequence + +from pydantic import BaseModel, EmailStr + + +class User(BaseModel): + id: str + forename: str + surname: str + email: EmailStr + table_name: ClassVar[str] = "user" + + +class UserCreate(BaseModel): + forename: str + surname: str + email: EmailStr + + +class UserUpdate(BaseModel): + forename: str + surname: str + email: EmailStr + + +class ResponseMessage(BaseModel): + message: str + + +class UserSearchResults(BaseModel): + results: Sequence[User] diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/tests/test_api.py b/apps/api/tests/test_api.py new file mode 100644 index 0000000..d855243 --- /dev/null +++ b/apps/api/tests/test_api.py @@ -0,0 +1,5 @@ +from src import __version__ + + +def test_version(): + assert __version__ == '0.1.0' diff --git a/apps/api/vercel.json b/apps/api/vercel.json new file mode 100644 index 0000000..75e99f2 --- /dev/null +++ b/apps/api/vercel.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "builds": [ + { + "src": "/src/main.py", + "use": "@vercel/python" + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "src/main.py" + } + ], + "env": { + "APP_MODULE": "src.main:app" + } +} diff --git a/apps/docs/.vscode/extensions.json b/apps/docs/.vscode/extensions.json new file mode 100644 index 0000000..9908def --- /dev/null +++ b/apps/docs/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["unifiedjs.vscode-mdx", "xyc.vscode-mdx-preview"] +} diff --git a/apps/docs/_images/deploy-api-configure-project.png b/apps/docs/_images/deploy-api-configure-project.png new file mode 100644 index 0000000..d6a95d1 Binary files /dev/null and b/apps/docs/_images/deploy-api-configure-project.png differ diff --git a/apps/docs/_images/deploy-api-running.png b/apps/docs/_images/deploy-api-running.png new file mode 100644 index 0000000..23a5b72 Binary files /dev/null and b/apps/docs/_images/deploy-api-running.png differ diff --git a/apps/docs/_images/deploy-docs-enable-auto-update.png b/apps/docs/_images/deploy-docs-enable-auto-update.png new file mode 100644 index 0000000..5f6585f Binary files /dev/null and b/apps/docs/_images/deploy-docs-enable-auto-update.png differ diff --git a/apps/docs/_images/deploy-docs-select-repo.png b/apps/docs/_images/deploy-docs-select-repo.png new file mode 100644 index 0000000..c39b35d Binary files /dev/null and b/apps/docs/_images/deploy-docs-select-repo.png differ diff --git a/apps/docs/_images/deploy-web-configure-project.png b/apps/docs/_images/deploy-web-configure-project.png new file mode 100644 index 0000000..6000894 Binary files /dev/null and b/apps/docs/_images/deploy-web-configure-project.png differ diff --git a/apps/docs/_images/deploy-web-ignored-build-step.png b/apps/docs/_images/deploy-web-ignored-build-step.png new file mode 100644 index 0000000..3fa83e9 Binary files /dev/null and b/apps/docs/_images/deploy-web-ignored-build-step.png differ diff --git a/apps/docs/_images/no-workspace.png b/apps/docs/_images/no-workspace.png new file mode 100644 index 0000000..ecc5d33 Binary files /dev/null and b/apps/docs/_images/no-workspace.png differ diff --git a/apps/docs/_images/run-and-debug.png b/apps/docs/_images/run-and-debug.png new file mode 100644 index 0000000..06bcbbd Binary files /dev/null and b/apps/docs/_images/run-and-debug.png differ diff --git a/apps/docs/_images/supabase-create-spells-table.png b/apps/docs/_images/supabase-create-spells-table.png new file mode 100644 index 0000000..05bc7fe Binary files /dev/null and b/apps/docs/_images/supabase-create-spells-table.png differ diff --git a/apps/docs/_images/supabase-create-user-table.png b/apps/docs/_images/supabase-create-user-table.png new file mode 100644 index 0000000..7e31c46 Binary files /dev/null and b/apps/docs/_images/supabase-create-user-table.png differ diff --git a/apps/docs/_images/supabase-credentials.png b/apps/docs/_images/supabase-credentials.png new file mode 100644 index 0000000..7f1a08f Binary files /dev/null and b/apps/docs/_images/supabase-credentials.png differ diff --git a/apps/docs/_images/supabase-new-table.png b/apps/docs/_images/supabase-new-table.png new file mode 100644 index 0000000..fb46e50 Binary files /dev/null and b/apps/docs/_images/supabase-new-table.png differ diff --git a/apps/docs/_images/supabase-project-create.png b/apps/docs/_images/supabase-project-create.png new file mode 100644 index 0000000..26524eb Binary files /dev/null and b/apps/docs/_images/supabase-project-create.png differ diff --git a/apps/docs/_images/supabase-upload-csv.png b/apps/docs/_images/supabase-upload-csv.png new file mode 100644 index 0000000..85c0da9 Binary files /dev/null and b/apps/docs/_images/supabase-upload-csv.png differ diff --git a/apps/docs/_images/tasks.png b/apps/docs/_images/tasks.png new file mode 100644 index 0000000..37befa3 Binary files /dev/null and b/apps/docs/_images/tasks.png differ diff --git a/apps/docs/_images/terminals.png b/apps/docs/_images/terminals.png new file mode 100644 index 0000000..3e69d57 Binary files /dev/null and b/apps/docs/_images/terminals.png differ diff --git a/apps/docs/_images/type-hinting.png b/apps/docs/_images/type-hinting.png new file mode 100644 index 0000000..77035d6 Binary files /dev/null and b/apps/docs/_images/type-hinting.png differ diff --git a/apps/docs/_images/workspace.png b/apps/docs/_images/workspace.png new file mode 100644 index 0000000..b6716fb Binary files /dev/null and b/apps/docs/_images/workspace.png differ diff --git a/apps/docs/api/spells/get-all-spells.mdx b/apps/docs/api/spells/get-all-spells.mdx new file mode 100644 index 0000000..282ffad --- /dev/null +++ b/apps/docs/api/spells/get-all-spells.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/spells/get-all/ +--- \ No newline at end of file diff --git a/apps/docs/api/spells/get-spell.mdx b/apps/docs/api/spells/get-spell.mdx new file mode 100644 index 0000000..620f7cc --- /dev/null +++ b/apps/docs/api/spells/get-spell.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/spells/get/ +--- \ No newline at end of file diff --git a/apps/docs/api/spells/search-spells.mdx b/apps/docs/api/spells/search-spells.mdx new file mode 100644 index 0000000..6b0cc56 --- /dev/null +++ b/apps/docs/api/spells/search-spells.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/spells/search/ +--- \ No newline at end of file diff --git a/apps/docs/api/users/create-user.mdx b/apps/docs/api/users/create-user.mdx new file mode 100644 index 0000000..b07b092 --- /dev/null +++ b/apps/docs/api/users/create-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /api/v1/users/create +--- \ No newline at end of file diff --git a/apps/docs/api/users/get-all-users.mdx b/apps/docs/api/users/get-all-users.mdx new file mode 100644 index 0000000..869228c --- /dev/null +++ b/apps/docs/api/users/get-all-users.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/users/get-all/ +--- \ No newline at end of file diff --git a/apps/docs/api/users/get-user.mdx b/apps/docs/api/users/get-user.mdx new file mode 100644 index 0000000..4eaa29b --- /dev/null +++ b/apps/docs/api/users/get-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/users/get/ +--- \ No newline at end of file diff --git a/apps/docs/api/users/search-users.mdx b/apps/docs/api/users/search-users.mdx new file mode 100644 index 0000000..0eb13db --- /dev/null +++ b/apps/docs/api/users/search-users.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /api/v1/users/search/ +--- \ No newline at end of file diff --git a/apps/docs/documentation/configuration/docs.mdx b/apps/docs/documentation/configuration/docs.mdx new file mode 100644 index 0000000..f7c36c8 --- /dev/null +++ b/apps/docs/documentation/configuration/docs.mdx @@ -0,0 +1,161 @@ +--- +title: Documentation config +"og:title": Mintlify setup and configuration +description: Mintlify setup and configuration +--- + +## Introduction + +The documentation site is built entirely using Mintlify. Mintlify docs are rendered from MDX files and configurations. Mintlify works with a Github integration to +constantly build an independent docs site based off your latest published repository. + +Documentation can be edited in raw MDX, or directly through the Mintlify dashboard (which stays in sync with your local Github repo). + +Mintlify is fast to setup and provides tonnes of features out-the-box without the need to pay for a subscription for the basic services. + +You can run the **docs site only** by running the `pnpm run dev` command from a terminal within the `docs` directory. + +## Docs structure + +The docs root directory is structured as follows: + +``` +docs +├── _images +├── api +├── configuration +├── deployment +├── logos +├── *.mdx +├── mint.json +└── package.json +``` + +The docs directory is relatively simple and is made mostly of MDX files. Some notable files are: + +| Item | Description | +| ------------ | ----------------------------------------------------------- | +| api | Automatically generated files (except for Introduction.mdx) | +| mint.json | Mintlify configuration object | +| package.json | Scripts needed in the monorepo/for generating the API files | + +## Creating API endpoint documentation + +We can leverage FastAPI's baked in OpenAPI support to automatically generate API documentation. + +Using Mintlify's `@mintlify/scraping` package, we can scrape our FastAPI's OpenAPI schema to generate an interactive API documentation site. + + + For a more detailed description, read the [official + documentation](https://mintlify.com/docs/api-playground/openapi/setup#create-mdx-files-for-openapi-endpoints) + + +### Generating OpenAPI schema + +#### Step 1: Generate MDX + +Run the `generate-api` scripts within the `package.json` to generate the required MDX +files. There are two scripts here, one configured to use the production +`OpenAPI.json` and the second configured to use the development version.\ +\ +It is typically preferred to use the production version once any changes to +your API have been made and published. + +#### Step 2: Update mint.json + + + + Upon generating the relevant API information, `@mintlify/scraping` helpfully outputs the suggested navigation to the terminal: + +```bash +navigation object suggestion: +[ + { + "group": "users", + "pages": [ + "api/users/get-user", + "api/users/get-all-users", + "api/users/search-users", + "api/users/create-user" + ] + }, + { + "group": "spells", + "pages": [ + "api/spells/get-spell", + "api/spells/get-all-spells", + "api/spells/search-spells" + ] + } +] +``` + +For sake of simplicity, copy this navigation object suggestion and add it your `mint.json` under the `navigation` object: + +```json +"navigation": [ + { + "group": "Getting Started", + "pages": ["documentation/introduction", "documentation/local-development"] + }, + { + "group": "Configuration", + "pages": [ + "documentation/configuration/turbo", + "documentation/configuration/fastapi", + "documentation/configuration/nextjs", + "documentation/configuration/docs" + ] + }, + { + "group": "Deployment", + "pages": [ + "documentation/deployment/vercel", + "documentation/deployment/deployment" + ] + }, + { + "group": "API Reference", + "pages": ["api/introduction"] + }, + { + "group": "Users", + "pages": [ + "api/users/get-user", + "api/users/get-all-users", + "api/users/search-users", + "api/users/create-user" + ] + }, + { + "group": "Spells", + "pages": [ + "api/spells/get-spell", + "api/spells/get-all-spells", + "api/spells/search-spells" + ] + } +], +``` + + + + Add the `openapi` key to your `mint.json`. This, preferably, can be linked to your published OpenAPI schema: + + ```json + { + ... + "openapi":"https://next-fast-turbo-api.vercel.app/openapi.json" + ... + } + ``` + + This enables the generated MDX files to describe and interact with your API. + + + This has been hit and miss in the past for me. If doing this does not auto-populate your API reference with the documentation, the solution is to manually\ + create an `openapi.json` file in the `docs` root. Manually copy and paste/save your `openapi.json` into this file. + + + + diff --git a/apps/docs/documentation/configuration/fastapi.mdx b/apps/docs/documentation/configuration/fastapi.mdx new file mode 100644 index 0000000..ddac7ee --- /dev/null +++ b/apps/docs/documentation/configuration/fastapi.mdx @@ -0,0 +1,377 @@ +--- +title: FastAPI config +"og:title": "FastAPI configuration and setup" +description: FastAPI configuration and setup +--- + +## Introduction + +The backend uses [FastAPI](https://fastapi.tiangolo.com/). FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints + +You can run the **api only** by running the `pnpm run dev` command from a terminal within the `api` directory. Alternatively, you can directly run the `run.py` file. + +## API structure + +The API root directory is structured as follows: + +``` +api +├── src +│ ├── api +│ ├── crud +│ ├── schemas +│ ├── __init__.py +│ ├── config.py +│ └── main.py +├── tests +├── .env +├── .env.example +├── harry-potter-db-seed-spells.csv +├── harry-potter-db-seed-users.csv +├── package.json +├── poetry.lock +├── pyproject.toml +├── requirements.txt +├── run.py +└── vercel.json +``` + +### Src directory + +The `src` directory is where all the application code sits. Below briefly explains each folder/file + +| Item | Description | +| --------- | ----------------------------------------------- | +| api | The API endpoints for the application | +| crud | The CRUD operations used within the application | +| schemas | The schemas used within the application | +| config.py | Main application configuration | +| main.py | Application | + +### Root directory + +The root directory contains the typical files one would expect to see in a Python project. The below files are worth describing: + +| Item | Description | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| package.json | Not typical in a Python programme; used due to the nature of the monorepo. Running `pnpm run dev` at the project root will execute the `dev` script in this `package.json` | +| vercel.json | The configuration for deploying the FastAPI aspect of the application to Vercel. | +| \*.csv | Simple seed data for the database, sourced from the [Harry Potter API](https://hp-api.onrender.com/) | + +## Dependencies + +### Python 3.9 + +Because the project is being deployed on Vercel, Python version `3.9` must be used as this is the latest supported version of Python. For more information, read the [official documentation](https://vercel.com/docs/functions/runtimes/python). + +If deploying to somewhere other than Vercel, check which version of Python you may use and adjust the project according to your needs. + +### Supabase + +The project uses [Supabase](https://supabase.com/) as a database. Since Planetscale [removed their free Hobby tier](https://planetscale.com/blog/planetscale-forever), +Supabase has seemed like a good alternative to use. You do not have to use Supabase with the backend, but the project is written from the perspective of using it. + + + If you are using Supabase, + [`supabase-py-async`](https://pypi.org/project/supabase-py-async/) is already + included as a project dependency within the `pyproject.toml`. If you are not + using Supabase, this can be removed. + + +### Poetry + +[Poetry](https://python-poetry.org/) is used to manage the virtual environment and project dependencies. +A `requirements.txt` has been generated to enable the installation of Python packages via the `pip install` command. + +If you do not use Poetry, you can remove the `poetry.lock` and `pyproject.toml` files. + +You will need a `requirements.txt` when deploying. Vercel also accepts a `Pipenv` file if you use Pipenv, otherwise, you'll need the `requirements.txt` for the `api` to build correctly + +## Adding your own endpoints + +Given the project structure, there are three areas that you must be aware of when adding your own endpoints with new models. The main areas are: + +- Schemas +- CRUD +- API + +### Example: Creation of Spells endpoints + +#### Step 1: Create a schema + + + + Add a new schema to the `schemas` directory. + + ```python src/schemas/spell.py + from typing import ClassVar, Sequence + + from pydantic import BaseModel + + + class Spell(BaseModel): + id: str + name: str + description: str + table_name: ClassVar[str] = "spells" + + + class SpellCreate(BaseModel): + id: str + name: str + description: str + + + class SpellUpdate(BaseModel): + id: str + name: str + description: str + + + class SpellSearchResults(BaseModel): + results: Sequence[Spell] + + ``` + + The `table_name` value needs to be included in the base `Spell` class. This **must** be the same as the name of the table created in Supabase. It is used in the CRUD operations to identify the table to work with. + + | Item | Description | + |--------------------|---------------------------------------------------------------------------------------------| + | `Spell` | The base class describing the columns that will be in the Supabase table | + | SpellCreate | The class for creating a new `Spell` | + | SpellUpdate | The class for updating an existing `Spell` | + | SpellSearchResults | Describes how data will be returned in the API response. It will be a `Sequence` of `Spell` | + + + + + To easily import the new `Spell` classes throughout our application, they need to be added to the `src/schemas/__init__.py` file. + + ```python src/schemas/__init__.py + from .user import User, UserCreate, UserSearchResults, UserUpdate + from .spell import Spell, SpellCreate, SpellSearchResults, SpellUpdate + ``` + + This allows us to import from `src/schemas` like so: + + ```python + from src.schemas import Spell, SpellCreate, SpellSearchResults, SpellUpdate + ``` + + + + +#### Step 2: Setup CRUD operations + +Specific CRUD operations can be created for each endpoint. However, generic CRUD operations in `src/crud/base.py` can also be used without modification. + + + + ```python src/crud/crud_spell.py + from fastapi import HTTPException + from supabase_py_async import AsyncClient + + from src.crud.base import CRUDBase + from src.schemas import Spell, SpellCreate, SpellUpdate + + + class CRUDSpell(CRUDBase[Spell, SpellCreate, SpellUpdate]): + async def get(self, db: AsyncClient, *, id: str) -> Spell | None: + try: + return await super().get(db, id=id) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"{e.code}: Spell not found. {e.details}", + ) + + async def get_all(self, db: AsyncClient) -> list[Spell]: + try: + return await super().get_all(db) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while fetching spells. {e}", + ) + + async def search_all( + self, db: AsyncClient, *, field: str, search_value: str, max_results: int + ) -> list[Spell]: + try: + return await super().search_all( + db, field=field, search_value=search_value, max_results=max_results + ) + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"An error occurred while searching for spells. {e}", + ) + + + spell = CRUDSpell(Spell) + ``` + The `CRUDSpell` class inherits from the `CRUDBase` class located in `src/crud/base.py`. The `Spell`, `SpellCreate` and `SpellUpdate` classes are passed to the `CRUDBase` class to specify the types of data that will be used in the CRUD operations. + + The operations that will be used in the API endpoints are functions of the `CRUDSpell` class. + + Finally, `spell` is an instance of `CRUDSpell`. We import this instance to use in the API endpoints like so: `spell.get_all(db)` or `spell.get(db, id=id)`. + + + **This part of the project is intended to be read-only**, however, the `SpellCreate` and `SpellUpdate` classes are created in the schema regardless as they are required by the `CRUDBase` model's function arguments. `CRUDBase` can be refactored to have optional parameters, or a read-only version of the `CRUDBase` class can be created. This is beyond the scope of this project. + + + + + + Similar to setting up the schemas, the `CRUDSpell` class needs to be added to the `src/crud/__init__.py` file. + + ```python src/crud/__init__.py + from .crud_user import user + from .crud_spell import spell + ``` + + + + +#### Step 3: Create the API endpoints + + + + Create the endpoints in the `src/api/api_v1/endpoints/spells.py` file. + + ```python src/api/api_v1/endpoints/spells.py + from typing import Literal, Optional, Union + + from fastapi import APIRouter, HTTPException + + from src.api.deps import SessionDep + from src.crud import spell + from src.schemas import Spell, SpellSearchResults + + router = APIRouter() + + + @router.get("/get/", status_code=200, response_model=Spell) + async def get_spell(session: SessionDep, spell_id: str) -> Spell: + """Returns a spell from a spell_id. + + **Returns:** + - spell: spell object. + """ + return await spell.get(session, id=spell_id) + + + @router.get("/get-all/", status_code=200, response_model=list[Spell]) + async def get_all_spells(session: SessionDep) -> list[Spell]: + """Returns a list of all spells. + + **Returns:** + - list[spell]: List of all spells. + """ + return await spell.get_all(session) + + + @router.get("/search/", status_code=200, response_model=SpellSearchResults) + async def search_spells( + session: SessionDep, + search_on: Literal["spells", "description"] = "spell", + keyword: Optional[Union[str, int]] = None, + max_results: Optional[int] = 10, + ) -> SpellSearchResults: + """ + Search for spells based on a keyword and return the top `max_results` spells. + + **Args:** + - keyword (str, optional): The keyword to search for. Defaults to None. + - max_results (int, optional): The maximum number of search results to return. Defaults to 10. + - search_on (str, optional): The field to perform the search on. Defaults to "email". + + **Returns:** + - SpellSearchResults: Object containing a list of the top `max_results` spells that match the keyword. + """ + if not keyword: + results = await spell.get_all(session) + return SpellSearchResults(results=results) + + results = await spell.search_all( + session, field=search_on, search_value=keyword, max_results=max_results + ) + + if not results: + raise HTTPException( + status_code=404, detail="No spells found matching the search criteria" + ) + + return SpellSearchResults(results=results) + ``` + + Here we are calling `spell` object which we setup in step 2. + + The `SessionDep` (located in `src/api/deps.py`) dependency is used to access the database. + + The `response_model` parameter is used to specify the type of data that will be returned from the endpoint. This is used to generate TypeScript types for the frontend. + + + + To include the new endpoints in the API, it must be added to the API router, located in `src/api/api_v1/api.py`. + + ```python src/api/api_v1/api.py + from fastapi import APIRouter + from src.api.api_v1.endpoints import users, spells + + api_router = APIRouter() + api_router.include_router(users.router, prefix="/users", tags=["users"], responses={404: {"description": "Not found"}}) + api_router.include_router(spells.router, prefix="/spells", tags=["spells"], responses={404: {"description": "Not found"}}) + ``` + + + + +#### Step 4: Create the table in Supabase + + + + Create a new table in your Supabase dashboard. It is **imperative** that the table name matches the `table_name` value in the `Spell` class in `src/schemas/spell.py`. + + Columns should be added to match the `Spell` class. For example, we have the following `Spell` class: + + ```python + class Spell(BaseModel): + id: str + name: str + description: str + table_name: ClassVar[str] = "spells" + ``` + + In this example, the table should be named `spells` (table names are case-sensitive) with the columns `id`, `name`, and `description`. \ + \ + For data types, `id` can be automatically assigned - in this example, it is set to a `uuid`. The `name` and `description` fields are strings, therefore their column's type is set to `text`. The `id` column should be the primary key. + You do not need to add the `table_name` column as this is used in the FastAPI code to identify the table to work with. + + + The data can be seeded using the Supabase dashboard by uploading the `harry-potter-db-seed-spells.csv` file. For a more detailed explanation, please see the [official documentation](https://supabase.com/docs/guides/database/import-data#option-1-csv-import-via-supabase-dashboard) + + + The table security should be configured to allow the FastAPI application to access the data. By default, Supabase will have [RLS (Row Level Security)](https://supabase.com/docs/guides/auth/row-level-security) enabled.\ + \ + If left un-configured, the database will return an empty array. As this is a simple read-only project, I am turning `RLS` off. However, for true CRUD operations, it should be configured to use authentication which is beyond the scope of this project. + + \ + To disable `RLS`, in the table settings, select `configure RLS` and then `disable RLS`.\ + + + Once the table has been created, you must ensure that the `DB_URL` and `DB_API_KEY` parameters are populated in your `.env` file located in the root of the `API` directory.\ + \ + These values come from the Supabase dashboard by going to `settings` and then `API`. Copy the Project URL (`DB_URL`) and the Project API Key (`DB_API_KEY`). + + + `DB_USERNAME` and `DB_PASSWORD` should also included in the `.env` as they are configured in the `src/config.py`. If you do not include these you will receive an error from Pydantic.\ + \ + Their inclusion is a pre-cursor to authentication, but they are not actually used in this project scaffold. + + + + + +#### Step 5: Test your new endpoints +You can now test your new endpoints using the FastAPI Swagger UI or by making requests to the API. diff --git a/apps/docs/documentation/configuration/nextjs.mdx b/apps/docs/documentation/configuration/nextjs.mdx new file mode 100644 index 0000000..976efa4 --- /dev/null +++ b/apps/docs/documentation/configuration/nextjs.mdx @@ -0,0 +1,176 @@ +--- +title: Next.js config +"og:title": "Next.js config and setup" +"description": "Next.js config and setup" +--- + +## Introduction + +The frontend uses the latest version of Next.js and TypeScript; it is configured using the latest [app router](https://nextjs.org/docs/app). + +You can run the **frontend only** by running the `pnpm run dev` command from a terminal within the `web` directory. Alternatively, you can run the entire application by running `pnpm run dev` from the **root directory**. + +## App structure + +The app root directory is structured as follows: + +``` +web +├── app +├── components +├── lib +├── public +├── .env +├── .env.example +├── .eslintrc.js +├── components.json +├── next.config.js +├── package.json +├── postcss.config.js +├── README.md +├── tailwind.config.js +├── tsconfig.json +└── yarn.lock +``` + +#### App + +| Item | Description | +| ----------- | ---------------------------------------------------------------------------------------------------- | +| globals.css | Tailwind global config. Uses a generated theme from [ShadCN UI themes](https://ui.shadcn.com/themes) | +| layout.tsx | App layout | +| page.tsx | Landing page | + +#### Components + +| Item | Description | +| ------- | ----------------------------------------------------------------------------------------------------------------------------- | +| layouts | The dashboard layout is stored which wraps the application `{children}` in `root/app/layout.tsx` | +| theme | The `ThemeProvider` component(s) that wrap the application. | +| ui | Where `ShadCN` components are stored upon being generated. Configured by the `components.json` located in the `frontend root` | +| \*.tsx | Components used within the application | + +#### Lib +| Item | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------- | +| api | The output generated by the `openapi` npm package. Configured in the `package.json` task named `generate-client` | +| config | Stores site config. Currently contains the setup for sidebar navigation | +| twConfig.ts | Tailwind colour config exported to an accessible Object. Used for assigning theme-aware colours in charts | +| utils.ts | ShadCN utils | + + + +## TypeScript from FastAPI +As FastAPI is based on the OpenAPI specification, you get automatic compatibility with many tools, including the automatic API docs (provided by Swagger UI). + +One particular advantage that is not necessarily obvious is that you can generate clients (sometimes called SDKs ) for your API, for many different programming languages. + +For a more detailed explanation, see the [official FastAPI documentation](https://fastapi.tiangolo.com/advanced/generate-clients/#generate-a-typescript-client-with-the-preprocessed-openapi) + +## Why generate TypeScript? +Generating a TypeScript client for your FastAPI backend is a great way to ensure that your frontend and backend are always in sync. It is also a way to provide type hinting while writing your frontend code without needing a permanent reference to the API, or re-creating the schema using something like Zod. + + + +### How to generate TypeScript + + + There are two generate-client tasks configured in the `package.json`: + ```JSON + "scripts": { + ... + "generate-client": "openapi --input https://next-fast-turbo.vercel.app/openapi.json --output ./lib/api/client --client axios --useOptions --useUnionTypes", + "generate-client:dev": "openapi --input http://127.0.0.0:8000/openapi.json --output ./lib/api/client --client axios --useOptions --useUnionTypes" + ... + }, + ``` + The `generate-client` task is set to run off the production OpenAPI schema.\ + \ + The `generate-client:dev` task is set to use the localhost OpenAPI schema. This is useful for development, as it will use the latest schema from the backend. + + Ensure your production API URL is configured, or that your local API URL is correct, and then run the relevant task. + + + + + A key file to be aware of is the `OpenAPI.ts` which is generated in the `lib/api/client/core` directory. This file has the main configuration for the API connection as below: + + ```tsx lib/api/client/core/OpenAPI.ts + export const OpenAPI: OpenAPIConfig = { + BASE: "http://127.0.0.0:8000", + VERSION: "0.1.0", + WITH_CREDENTIALS: false, + CREDENTIALS: "include", + TOKEN: undefined, + USERNAME: undefined, + PASSWORD: undefined, + HEADERS: undefined, + ENCODE_PATH: undefined, + }; + ``` + + The problem with this is that we would want to differentiate between which API is being used. When the TypeScript is generated, the given OpenAPI URL is used as the base URL for the API. + This is not ideal for production, as you would want to use the production API URL, and vice versa in development. + + You can override this by adding the following to your root `layout.tsx` file: + + ```tsx layout.tsx + import { OpenAPI } from "@/lib/api/client"; + + if (process.env.NODE_ENV === "production") { + OpenAPI.BASE = "https://next-fast-turbo.vercel.app" + } + + ``` + + + + + +## Using the generated API +Once you have generated your TypeScript interface to your API, you can use it in your frontend code. + +In its simplest form, you can import the generated API and use it as below: + +```tsx +import { UsersService } from "@/lib/api/client"; + +const response = await UsersService.usersSearchUsers({ + keyword: "keyword", + searchOn: "searchOn", + maxResults: "maxResults", + }); +``` + +You can see this code being used in the `components/search-users.tsx` file, as part of the form's onSubmit function: + +```tsx + const onSubmit = async (data: z.infer) => { + try { + const maxResults = data.searchResults ? parseInt(data.searchResults) : 10; + const response = await UsersService.usersSearchUsers({ + keyword: data.keyword, + searchOn: data.searchOn, + maxResults: maxResults, + }); + setSearchResults(response); + setError(null); + } catch (error) { + setSearchResults({ results: [] }); + setError(error); + } + }; +``` +This code also imports the `UserSearchResults` type which is used to set the state of the search results: +```tsx +import { UsersService, UserSearchResults } from "@/lib/api/client"; + +const [searchResults, setSearchResults] = React.useState({ + results: [], + }); +``` + + +## Important considerations +- If you make changes to your API, you will need to re-generate the TypeScript interface. +- Re-generating this interface will overwrite the existing files. If you want to modify anything (e.g. `api/client/core/OpenAPI.ts`) and have it persist, do this outside of the generated files. You can see this in action in the `root/app/layout.tsx` file. \ No newline at end of file diff --git a/apps/docs/documentation/configuration/turbo.mdx b/apps/docs/documentation/configuration/turbo.mdx new file mode 100644 index 0000000..156d69c --- /dev/null +++ b/apps/docs/documentation/configuration/turbo.mdx @@ -0,0 +1,53 @@ +--- +title: Turbo config +"og:title": "Configuring Turbo for FastAPI, Next.js and Turborepo" +description: Configuring Turbo for FastAPI, Next.js and Turborepo. +--- + +## Introduction +This project was first created using the `create-turbo` npm package: + + + ```bash npm + npx create-turbo@latest + ``` + + ```bash yarn + yarn dlx create-turbo@latest + ``` + + ```bash pnpm + pnpm dlx create-turbo@latest + ``` + + +It is recommended that you read through the [official documentation](https://turbo.build/repo/docs/getting-started/create-new) to understand the breakdown of the monorepo. + +## Changes to the official Turbo starter +The official Turbo starter is a great starting point for a monorepo, but it has some configurations I did not need. As I will not be sharing code between the backend and frontend (nor the docs), I have **removed** the `packages/ui` directory. + +If you want to **add a new package** to the project, please reference the [official documentation](https://turbo.build/repo/docs/handbook/sharing-code/internal-packages). + +For quick reference: + 1. Create a new folder in `packages/` + 2. Add a `package.json`, with `name` and `types` pointing at `src/index.ts[x]` + 3. Add `src/index.ts[x]`, with at least one named export + 4. Install your packages from `root` + + +## Turbo.json +The currently configured `turbo.json` does not have many changes from the official starter. The only change is the addition of the `globalEnv` key. + +```json +{ + "globalEnv": [ + "NODE_ENV" + ], +} +``` +Without adding these environment variables to the `globalEnv`, you will receive an eslint error when referencing variables in your `.env`, however, your code will still work. + + +There are different ways that people solve this. While it's possible to use\ +`"globalDependencies": ["**/.env"]`, that did not work in this project.\ + \ No newline at end of file diff --git a/apps/docs/documentation/deployment/deployment.mdx b/apps/docs/documentation/deployment/deployment.mdx new file mode 100644 index 0000000..4ee0152 --- /dev/null +++ b/apps/docs/documentation/deployment/deployment.mdx @@ -0,0 +1,131 @@ +--- +title: Deploying the monorepo +description: Deploying your monorepo to Vercel/Mintlify +"og:title": Deploying your monorepo to Vercel/Mintlify +--- + +## Introduction + +Deploying your monorepo to Vercel is relatively straightforward, but there are a handful of things to be aware of. +The frontend deployment is relatively straightforward, but the backend deployment can be a little more complex and need a bit more attention. + +For a more detailed guide on deploying a monorepo to Vercel, see the [official documentation](https://vercel.com/docs/monorepos/turborepo). + +## Prerequisites +Before continuing, ensure you have: +- A [Vercel](https://vercel.com/) account. It is typically easiest to sign up with your GitHub account, as we'll also be deploying directly from the GitHub repository. +- Published your repo to GitHub. If you haven't done this yet, you can follow the instructions [here](https://docs.github.com/en/get-started/quickstart/create-a-repo). +- Optional. If you're planning to deploy your documentation site to Mintlify, you'll need to have a [Mintlify](https://mintlify.com/) account. + +## Frontend Deployment + + + Go to your [Vercel dashboard](https://vercel.com/dashboard) and click the `Add New...` button, and select `Project`. + + + + Select the `Import Git Repository` option and select your repository from the list of repositories. + + + + Vercel will automatically detect that you have a monorepo, and should have correctly populated all the required fields. + + The project root directory should be automatically detected, but if it isn't, you can manually set it to the folder where your frontend is stored. In this project, that is `apps/web`. + + + + + + + + Click the `Deploy` button to deploy your project. + + + + We don't want to continuously rebuild our frontend every time we push changes to the backend or documentation. To prevent this, we can add an ignored build step.\ + \ + From the newly deployed frontend dashboard, navigate to `Settings`, then to `Git`. Scroll down to the `Ignored Build Step` section and set the behaviour to + **Only build Turborepo app if there are changes**. + + + + + + For more information, see the [official documentation](https://vercel.com/docs/projects/overview#ignored-build-step). + + + +## Backend deployment + + + Go to your [Vercel dashboard](https://vercel.com/dashboard) and click the `Add New...` button, and select `Project`. + + + + Select the `Import Git Repository` option and select your repository from the list of repositories. + + + + Vercel will automatically detect that you have a monorepo, but for the backend we need to change the default configuration. + + - Configure your project name + - Change the `Framework Preset` to `Other` (there is currently no Python option) + - Change the `Root Directory` to the folder where your backend is stored. In this project, that is `apps/api` + - Add your Environment Variables + + + + + + + + Click the `Deploy` button to deploy your project. Once built, the API should be running at the URL provided. + + + + + + + + We don't want to continuously rebuild our frontend every time we push changes to the backend or documentation. To prevent this, we can add an ignored build step.\ + \ + From the newly deployed frontend dashboard, navigate to `Settings`, then to `Git`. Scroll down to the `Ignored Build Step` section and set the behaviour to + **Only build Turborepo app if there are changes**. + + + + + + For more information, see the [official documentation](https://vercel.com/docs/projects/overview#ignored-build-step). + + + + +## Documentation deployment +For a more detailed explanation, visit the [offical documentation](https://mintlify.com/docs/quickstart) + +Connecting to an existing repository with Mintlify is a little bit tricky, as their walkthrough currently seems to only support creating a new repository. However, it is possible to connect an existing repository by following the steps below. + + + + Sign in to [Mintlify](https://mintlify.com/). You will further be prompted to **Sign in with GitHub**. Follow the onscreen instructions. + + + + Configure your deployment to be pointed to your `docs` directory. Ensure **set up as a monorepo** is selected. + + + + + + + + From the Mintlify dashboard, click **Things to Do** and then click **Enable automatic updates**. + + + + + + + + diff --git a/apps/docs/documentation/introduction.mdx b/apps/docs/documentation/introduction.mdx new file mode 100644 index 0000000..2a7194a --- /dev/null +++ b/apps/docs/documentation/introduction.mdx @@ -0,0 +1,70 @@ +--- +title: Introduction +"og:title": "Getting started with Next-Fast-Turbo" +description: A starter project for FastAPI, Next.js and Turborepo. +--- + +## What is Next-Fast-Turbo? + +Next-Fast-Turbo is designed as a personal starter kit for Next.js and FastAPI projects. It is a monorepo that includes a Next.js frontend and a FastAPI backend. The project is designed to be deployed to Vercel, but can be deployed to any platform that supports monorepos. + +This documentation is not written to be a tutorial, but instead to be a reference for the project. It is designed to be a living document that can be updated as the project evolves. + +## Features + +**Frontend**\ +The Next.js application comes with a fully built frontend that includes: + +- A responsive layout +- A dashboard/sidebar design +- Pre-configured connection to the backend API +- Autogenerated TypeScript types based off the FastAPI OpenAPI schema +- A variety of design components, mostly from ShadCN UI (including chart examples) + +**Backend**\ +The FastAPI application comes with a fully built backend that includes: + +- Example endpoints +- Pre-configured schema/crud operations +- Easily extensible to add more endpoints + +**Documentation**\ +Built using Mintlify, a fully responsive and configured documentation site that features: + +- A fully built documentation site +- A variety of Mintlify components +- A fully configured mint.json + +## Tech stack + +Next-Fast-Turbo is fully open-source built using the following technologies: + +**Frontend** + +- [Next.js](https://nextjs.org/) - Framework for building React applications +- [Tailwind CSS](https://tailwindcss.com/) - CSS framework +- [ShadCN UI](https://ui.shadcn.com/) - UI kit + +**Backend** + +- [FastAPI](https://fastapi.tiangolo.com/) - Python backend API + +**Documentation** + +- [Mintlify](https://mintlify.io/) - Documentation + +**Global** + +- [Vercel](https://vercel.com/) - hosting +- [Turbo](https://turbo.build/repo) - monorepo + +## Getting started + + + + Install the application locally + + + Deploy the monorepo to Vercel + + diff --git a/apps/docs/documentation/local-development.mdx b/apps/docs/documentation/local-development.mdx new file mode 100644 index 0000000..88f39cd --- /dev/null +++ b/apps/docs/documentation/local-development.mdx @@ -0,0 +1,308 @@ +--- +title: "Local Development" +"og:title": "How to setup local development" +description: "A guide on how to run the codebase locally." +--- + +## Introduction + +Next-Fast-Turbo 's codebase is set up in a monorepo (via [Turborepo](https://turbo.build/repo)) and is fully open-source. +Here's the monorepo structure: + +``` +apps +├── api +├── docs +├── web +packages +├── eslint-config +├── typescript-config +``` + +The `apps` directory contains the code for: + +- `web`: The frontend of the Next-Fast-Turbo's application +- `api`: Next-Fast-Turbo's FastAPI backend - written in Python +- `docs`: Next-Fast-Turbo's documentation site + +The `packages` directory contains the code for: + +- `eslint-config`: ESLint configurations for Next-Fast-Turbo's codebase. Boilerplate code included as part of the [create Turbo](https://turbo.build/repo/docs/getting-started/create-new) command +- `typescript-config`: TypeScript configurations for Next-Fast-Turbo's codebase. Boilerplate code included as part of the [create Turbo](https://turbo.build/repo/docs/getting-started/create-new) command + +## Running Next-Fast-Turbo +### Step 1: Local setup + + + + Clone the [Next-Fast-Turbo repo](https://github.com/cording12/next-fast-turbo.git). + + ```bash + git clone https://github.com/cording12/next-fast-turbo.git app-name + ``` + + + + Change to the root directory of the cloned repository and install the dependencies using the following command: + + ```bash + cd app-name + pnpm install + ``` + + + + It is recommended to use the pre-configured Workspace stored in the `.vscode` folder at the project root. + + Navigate to `app-name/.vscode/` and double click `next-fast-turbo.code-workspace` to open in VS Code, or, in VS Code navigate to **File** and then **Open Workspace from File**. + + You can rename this to match your project name. The extension, `code-workspace`, must stay the same, but it can be changed to `app-name.code-workspace` + + + +### Step 2: Python setup + + In a monorepo, VS Code sometimes uses the wrong Python interpreter, leading to **module not found** errors. You can open the `api` folder in its own VS Code window, but using + the pre-configured Workspace is recommended. + +While working on the Python backend, ensure that your terminal is activated in the correct folder. From the root, run the following command to change to the `api` directory: + +```bash +cd apps/api +``` + + + + Create a virtual environment in the `api` directory: + + + ```bash Poetry + poetry install + ``` + + ```bash Pip + python -m venv .venv + ``` + + + + If you're using Poetry, you could receive an error noting incorrect format of the `poetry.lock` file. This is a version mismatch between the version installed and the version used to generate the lock file. You can fix this by deleting the `poetry.lock` file + and running `poetry install` again. + + + + + + Run the following command to install the Python dependencies: + + ```bash + pip install -r requirements.txt + ``` + + + + Create a `.env` file in the `api` directory and add the following environment variables: + + ```env + DB_URL=supabase_url + DB_API_KEY=supabase_api_key + DB_EMAIl=email_address + DB_PASSWORD=password + ``` + + These can be placeholder values for now, but you'll need to replace them with your actual Supabase credentials (covered in step 3). + + + + + +### Step 3: Creating tables in Supabase +Next-Fast-Turbo uses [Supabase](https://supabase.com/) as the database for the backend. You'll need to create a new project in Supabase and then create the required tables. To get this example running, you need to only create two tables in Supabase. + + + + Visit [Supabase](https://supabase.com/) and register an account. Once you're logged in, create a new project and give it a name. + + + + + + + While your project is building, copy the `Project API Key` and `URL` values and add these to the `.env` file in the `api` directory, as described in step 3 of the [Python setup](#step-2-python-setup). + + + + + + + The tables are seeded with the two `.csv` files located in the `api` root, but the tables must be created before seeding. + + From the dashboard, visit the `Table Editor` and click the `New table` button. + + + + + Create the `users` and `spells` tables with columns that match their respective CSV columns. Below is how they are both configured: + + + + + + + + + + + + + + + RLS is set to disabled on these tables. Authentication with Supabase was not in the scope for this project, but you will want to configure this yourself for anything more than this simple example. + You can read more about [RLS](https://supabase.com/docs/guides/auth/row-level-security) in the Supabase documentation. + + + + + Once the tables are created, you can seed them with the data from the `.csv` files. From the `Table Editor`, click the `Insert` button and select the relevant `.csv` file to upload. + + + + + + + +### Step 4: Configure Turbo remote caching (optional) +Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines. + +By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. + + + + From the project root, run the command: + ```bash + npx turbo login + ``` + This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview). + + + Link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo: + ```bash + npx turbo link + ``` + + + +### Step 5: Running everything +To make the most of Turbo's monorepo structure, you can run the frontend, backend and documentation site simultaneously. From the root, run the following command: + +```bash root +pnpm run dev +``` +You can still run each separately by running the task directly from the relevant `package.json` or by running the `pnpm run dev` command from a terminal activated in the desired target location + + +## Working with a monorepo in VS Code + +For a better development experience, you can use VS Code Workspaces for the monorepo. This will allow you to run tasks and debug the codebase from a single window, while keeping things more organised. + +Furthermore, VS Code doesn't handle Python virtual environments particularly well when working within a monorepo. Running the `dev` command from the project root can make VS Code use your global Python installation, +instead of the `.venv` created in the `api` root. By using a Workspace, this alleviates the problem. + +For a more detailed guide on setting up a monorepo in VS Code, check out the [official Multi-root Workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) documentation + +### Step 1: Open the monorepo +In the `/.vscode/` directory, you'll find a `next-fast-turbo.code-workspace` file. Open this file in VS Code to open the monorepo Workspace. + + + + + + + + + + + + + + + +### Step 2: Running tasks +VS Code will try to autodetect tasks from gulp, grunt, npm, and TypeScript project files across all folders in a workspace as well as search for tasks defined in tasks.json files. The location of tasks is indicated by a folder name suffix + + + + + +From the above example, you can see there are several configured tasks with the relevant folder name after the task name. + +### Step 3: Debugging +With multi-root workspaces, VS Code searches across all folders for `launch.json` debug configuration files and displays them with the folder name as a suffix. +Additionally VS Code will also display launch configurations defined in the workspace configuration file. + + + + + +You can still create [launch configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) for each individual package in the monorepo and they will populate in the dropdown list automatically. + +#### Workspace launch configurations +If you want to create a Workspace level configuration with [compound launch](https://code.visualstudio.com/docs/editor/debugging#_compound-launch-configurations), you can edit the `next-fast-turbo.code-workspace` file and add the configurations you wish to launch. + +You can also edit the Workspace configuration file via the Command Palette\ +(Windows: Ctrl + Shift + P) and searching for `open workspace config` + +A compound launch configuration can reference the individual launch configurations by name as long as the names are unique within the workspace, for example: + +```json +{ + "launch": { + "version": "0.2.0", + "configurations": [], + "compounds": [ + { + "name": "Launch Frontend and Backend", + "configurations": ["Next.js: Chrome", "Python: FastAPI"] + } + ] + } +} +``` + +For a more detailed explanation, check out the [official documentation](https://code.visualstudio.com/docs/editor/multi-root-workspaces#_workspace-launch-configurations) + +### Optional: Extensions + + + Helps VS Code identify the correct Python virtual environment when installed in the working directory. This is especially useful when working with Python in a monorepo, as it can be difficult for VS Code to manage multiple virtual environments. + + [Python Envy](https://marketplace.visualstudio.com/items?itemName=teticio.python-envy) + + + + + + Terminal management in a monorepo can become cumbersome. This extension automatically creates a terminal in each of your monorepo's directories and names them accordingly. This will allow you to run commands and tasks from a terminal that's already set up in the correct directory. + + [Workspace Terminals](https://marketplace.visualstudio.com/items?itemName=joshx.workspace-terminals) + + + + +## Next Steps + + + + Configuring Turbo for your monorepo + + + Configuring FastAPI + + + Configuring Next.js + + + Configuring Mintlify for documentation + + + diff --git a/apps/docs/logos/favicon-placeholder.png b/apps/docs/logos/favicon-placeholder.png new file mode 100644 index 0000000..a64d760 Binary files /dev/null and b/apps/docs/logos/favicon-placeholder.png differ diff --git a/apps/docs/logos/logo-light.svg b/apps/docs/logos/logo-light.svg new file mode 100644 index 0000000..34b9008 --- /dev/null +++ b/apps/docs/logos/logo-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/logos/logo.svg b/apps/docs/logos/logo.svg new file mode 100644 index 0000000..ace6683 --- /dev/null +++ b/apps/docs/logos/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/mint.json b/apps/docs/mint.json new file mode 100644 index 0000000..24a7214 --- /dev/null +++ b/apps/docs/mint.json @@ -0,0 +1,96 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Next-Fast-Turbo Docs", + "favicon": "/logos/logo.svg", + "logo": { + "light": "/logos/logo.svg", + "dark": "/logos/logo-light.svg" + }, + "colors": { + "primary": "#7c3aed", + "light": "#af87ff", + "dark": "#1e293b", + "background": { + "dark": "#030712", + "light": "#ffffff" + } + }, + "modeToggle": { + "default": "dark" + }, + "topbarCtaButton": { + "name": "Dashboard", + "url": "https://next-fast-turbo-web.vercel.app/" + }, + "tabs": [ + { + "name": "API Reference", + "url": "api" + } + ], + "anchors": [ + { + "name": "Documentation", + "icon": "book-open-cover", + "url": "documentation" + }, + { + "name": "API Reference", + "icon": "rectangle-terminal", + "url": "api" + } + ], + "navigation": [ + { + "group": "Getting Started", + "pages": [ + "documentation/introduction", + "documentation/local-development" + ] + }, + { + "group": "Configuration", + "pages": [ + "documentation/configuration/turbo", + "documentation/configuration/fastapi", + "documentation/configuration/nextjs", + "documentation/configuration/docs" + ] + }, + { + "group": "Deployment", + "pages": [ + "documentation/deployment/deployment" + ] + }, + { + "group": "Users", + "pages": [ + "api/users/get-user", + "api/users/get-all-users", + "api/users/search-users", + "api/users/create-user" + ] + }, + { + "group": "Spells", + "pages": [ + "api/spells/get-spell", + "api/spells/get-all-spells", + "api/spells/search-spells" + ] + } + ], + "api": { + "maintainOrder": true, + "baseUrl": "https://next-fast-turbo-api.vercel.app" + }, + "openapi": "https://next-fast-turbo-api.vercel.app/openapi.json", + "feedback": { + "thumbsRating": true + }, + "footerSocials": { + "github": "https://github.com/cording12/next-fast-turbo", + "linkedin": "https://www.linkedin.com/in/jon-cording/" + } +} \ No newline at end of file diff --git a/apps/docs/openapi.json b/apps/docs/openapi.json new file mode 100644 index 0000000..c3cb3bf --- /dev/null +++ b/apps/docs/openapi.json @@ -0,0 +1,545 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI App", + "description": "A simple FastAPI app", + "version": "0.1.0" + }, + "servers": [ + { + "url": "https://next-fast-turbo-api.vercel.app" + } + ], + "paths": { + "/api/v1/users/get/": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User", + "description": "Returns a user from a user_id.\n\n**Returns:**\n- User: User object.", + "operationId": "users-get_user", + "parameters": [ + { + "name": "user_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/get-all/": { + "get": { + "tags": [ + "users" + ], + "summary": "Get All Users", + "description": "Returns a list of all users.\n\n**Returns:**\n- list[User]: List of all users.", + "operationId": "users-get_all_users", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array", + "title": "Response Users-Get All Users" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, + "/api/v1/users/search/": { + "get": { + "tags": [ + "users" + ], + "summary": "Search Users", + "description": "Search for users based on a keyword and return the top `max_results` items.\n\n**Args:**\n- keyword (str, optional): The keyword to search for. Defaults to None.\n- max_results (int, optional): The maximum number of search results to return. Defaults to 10.\n- search_on (str, optional): The field to perform the search on. Defaults to \"email\".\n\n**Returns:**\n- UserSearchResults: Object containing a list of the top `max_results` items that match the keyword.", + "operationId": "users-search_users", + "parameters": [ + { + "name": "search_on", + "in": "query", + "required": false, + "schema": { + "enum": [ + "id", + "email", + "forename", + "surname" + ], + "type": "string", + "default": "email", + "title": "Search On" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Keyword" + } + }, + { + "name": "max_results", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10, + "title": "Max Results" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSearchResults" + } + } + } + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/create": { + "post": { + "tags": [ + "users" + ], + "summary": "Create User", + "description": "Craete a new user.\n\n**Args:**\n- user_in (UserCreate): JSON of the user to create. Forename, surname and email. Email must be unique.\n\n**Returns:**\n- User: User object", + "operationId": "users-create_user", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/spells/get/": { + "get": { + "tags": [ + "spells" + ], + "summary": "Get Spell", + "description": "Returns a spell from a spell_id.\n\n**Returns:**\n- spell: spell object.", + "operationId": "spells-get_spell", + "parameters": [ + { + "name": "spell_id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Spell Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Spell" + } + } + } + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/spells/get-all/": { + "get": { + "tags": [ + "spells" + ], + "summary": "Get All Spells", + "description": "Returns a list of all spells.\n\n**Returns:**\n- list[spell]: List of all spells.", + "operationId": "spells-get_all_spells", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Spell" + }, + "type": "array", + "title": "Response Spells-Get All Spells" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, + "/api/v1/spells/search/": { + "get": { + "tags": [ + "spells" + ], + "summary": "Search Spells", + "description": "Search for spells based on a keyword and return the top `max_results` items.\n\n**Args:**\n- keyword (str, optional): The keyword to search for. Defaults to None.\n- max_results (int, optional): The maximum number of search results to return. Defaults to 10.\n- search_on (str, optional): The field to perform the search on. Defaults to \"email\".\n\n**Returns:**\n- spellSearchResults: Object containing a list of the top `max_results` items that match the keyword.", + "operationId": "spells-search_spells", + "parameters": [ + { + "name": "search_on", + "in": "query", + "required": false, + "schema": { + "enum": [ + "id", + "spells", + "description" + ], + "type": "string", + "default": "spells", + "title": "Search On" + } + }, + { + "name": "keyword", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Keyword" + } + }, + { + "name": "max_results", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10, + "title": "Max Results" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpellSearchResults" + } + } + } + }, + "404": { + "description": "Not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "Spell": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + } + }, + "type": "object", + "required": [ + "id", + "name", + "description" + ], + "title": "Spell" + }, + "SpellSearchResults": { + "properties": { + "results": { + "items": { + "$ref": "#/components/schemas/Spell" + }, + "type": "array", + "title": "Results" + } + }, + "type": "object", + "required": [ + "results" + ], + "title": "SpellSearchResults" + }, + "User": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "forename": { + "type": "string", + "title": "Forename" + }, + "surname": { + "type": "string", + "title": "Surname" + }, + "email": { + "type": "string", + "format": "email", + "title": "Email" + } + }, + "type": "object", + "required": [ + "id", + "forename", + "surname", + "email" + ], + "title": "User" + }, + "UserCreate": { + "properties": { + "forename": { + "type": "string", + "title": "Forename" + }, + "surname": { + "type": "string", + "title": "Surname" + }, + "email": { + "type": "string", + "format": "email", + "title": "Email" + } + }, + "type": "object", + "required": [ + "forename", + "surname", + "email" + ], + "title": "UserCreate" + }, + "UserSearchResults": { + "properties": { + "results": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array", + "title": "Results" + } + }, + "type": "object", + "required": [ + "results" + ], + "title": "UserSearchResults" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + } + } +} \ No newline at end of file diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 0000000..759a3fd --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,13 @@ +{ + "name": "docs", + "version": "0.1.0", + "scripts": { + "dev": "mintlify dev --port 3001", + "generate-api": "npx @mintlify/scraping@latest openapi-file https://next-fast-turbo-api.vercel.app/openapi.json --outDir ./api/", + "generate-api:dev": "npx @mintlify/scraping@latest openapi-file https://127.0.0.1:8000/openapi.json --outDir ./api/" + }, + "dependencies": { + "@mintlify/scraping": "^3.0.88", + "mintlify": "^4.0.127" + } +} \ No newline at end of file diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js new file mode 100644 index 0000000..7d644a4 --- /dev/null +++ b/apps/web/.eslintrc.js @@ -0,0 +1,9 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ["@repo/eslint-config/next.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, +}; diff --git a/apps/web/.vscode/launch.json b/apps/web/.vscode/launch.json new file mode 100644 index 0000000..3804a07 --- /dev/null +++ b/apps/web/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: Chrome", + "type": "node-terminal", + "request": "launch", + "command": "pnpm run dev", + "serverReadyAction": { + "pattern": "- Local:.+(https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome", + "webRoot": "${workspaceFolder}" + } + } + ] +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css new file mode 100644 index 0000000..6c05562 --- /dev/null +++ b/apps/web/app/globals.css @@ -0,0 +1,13 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; +@import "../components/theme/theme.css"; + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx new file mode 100644 index 0000000..8ffd0bd --- /dev/null +++ b/apps/web/app/layout.tsx @@ -0,0 +1,49 @@ +import "./globals.css"; +import type { Metadata } from "next"; +import { Inter as FontSans } from "next/font/google"; +import { DashboardLayout } from "@/components/layouts/dashboard"; +import { cn } from "@/lib/utils"; +import { ThemeProvider } from "@/components/theme/theme-provider"; +import { OpenAPI } from "@/lib/api/client"; +import { TailwindIndicator } from "@/components/tailwind-indicator"; + +export const fontSans = FontSans({ + subsets: ["latin"], + variable: "--font-sans", +}); + +if (process.env.NODE_ENV === "production") { + OpenAPI.BASE = "https://next-fast-turbo.vercel.app"; +} + +console.log("Using OpenAPI.base", OpenAPI.BASE); + +export const metadata: Metadata = { + title: "Next-Fast-Turbo", + description: "A Next.js, FastAPI and Turbo project scaffol", + icons: { + icon: ["/favicon.png"], + }, +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}): JSX.Element { + return ( + + + + {children} + + + + + ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx new file mode 100644 index 0000000..6e09c03 --- /dev/null +++ b/apps/web/app/page.tsx @@ -0,0 +1,11 @@ +import { CardsStats } from "./placeholder-stats"; +import SearchUsers from "@/components/search-users"; + +export default async function Page() { + return ( +
+ + +
+ ); +} diff --git a/apps/web/app/placeholder-stats.tsx b/apps/web/app/placeholder-stats.tsx new file mode 100644 index 0000000..ada9498 --- /dev/null +++ b/apps/web/app/placeholder-stats.tsx @@ -0,0 +1,17 @@ +// Example cards from ShadCN: https://github.com/shadcn-ui/ui/tree/0fae3fd93ae749aca708bdfbbbeddc5d576bfb2e/apps/www/registry/default/example/cards +import { FlexWrapper } from "@/components/flex-wrapper"; +import { DemoRevenue } from "@/components/demo-revenue"; +import { DemoSubscriptions } from "@/components/demo-subscriptions"; +import { DemoExercise } from "@/components/demo-exercise"; +import { DemoGoal } from "@/components/demo-goal"; + +export function CardsStats() { + return ( + + + + + + + ); +} diff --git a/apps/web/app/settings/page.tsx b/apps/web/app/settings/page.tsx new file mode 100644 index 0000000..f6ffccf --- /dev/null +++ b/apps/web/app/settings/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return
Settings page
; +} diff --git a/apps/web/components.json b/apps/web/components.json new file mode 100644 index 0000000..811d2db --- /dev/null +++ b/apps/web/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/apps/web/components/demo-exercise.tsx b/apps/web/components/demo-exercise.tsx new file mode 100644 index 0000000..afa465a --- /dev/null +++ b/apps/web/components/demo-exercise.tsx @@ -0,0 +1,126 @@ +"use client"; +// Example data from ShadCN: https://github.com/shadcn-ui/ui/blob/0fae3fd93ae749aca708bdfbbbeddc5d576bfb2e/apps/www/registry/default/example/cards/stats.tsx#L61 + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Line, LineChart, ResponsiveContainer, Tooltip } from "recharts"; +import { twColourConfig } from "@/lib/twConfig"; + +const timeSeriesData = [ + { + average: 400, + today: 240, + }, + { + average: 300, + today: 139, + }, + { + average: 200, + today: 980, + }, + { + average: 278, + today: 390, + }, + { + average: 189, + today: 480, + }, + { + average: 239, + today: 380, + }, + { + average: 349, + today: 430, + }, +]; + +export function DemoExercise() { + return ( + + + Exercise Minutes + + Your exercise minutes are ahead of where you normally are. + + + + {/*
*/} +
+ + + { + if (active && payload && payload.length) { + return ( +
+
+
+ + Average + + + {payload[0].value} + +
+
+ + Today + + + {payload[1].value} + +
+
+
+ ); + } + return null; + }} + /> + + +
+
+
+ + + ); +} diff --git a/apps/web/components/demo-goal.tsx b/apps/web/components/demo-goal.tsx new file mode 100644 index 0000000..01fc427 --- /dev/null +++ b/apps/web/components/demo-goal.tsx @@ -0,0 +1,120 @@ +"use client"; +// https://github.com/shadcn-ui/ui/blob/0fae3fd93ae749aca708bdfbbbeddc5d576bfb2e/apps/www/registry/default/example/cards/activity-goal.tsx +import * as React from "react"; +import { Minus, Plus } from "lucide-react"; +import { Bar, BarChart, ResponsiveContainer } from "recharts"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { twColourConfig } from "@/lib/twConfig"; + +const data = [ + { + goal: 400, + }, + { + goal: 300, + }, + { + goal: 200, + }, + { + goal: 300, + }, + { + goal: 200, + }, + { + goal: 278, + }, + { + goal: 189, + }, + { + goal: 239, + }, + { + goal: 300, + }, + { + goal: 200, + }, + { + goal: 278, + }, + { + goal: 189, + }, + { + goal: 349, + }, +]; + +export function DemoGoal() { + const [goal, setGoal] = React.useState(350); + + function onClick(adjustment: number) { + setGoal(Math.max(200, Math.min(400, goal + adjustment))); + } + + return ( + + + Move Goal + Set your daily activity goal. + + +
+ +
+
{goal}
+
+ Calories/day +
+
+ +
+
+ + + + + +
+
+ + + +
+ ); +} diff --git a/apps/web/components/demo-revenue.tsx b/apps/web/components/demo-revenue.tsx new file mode 100644 index 0000000..1622b30 --- /dev/null +++ b/apps/web/components/demo-revenue.tsx @@ -0,0 +1,82 @@ +"use client"; +// Example data from ShadCN: https://github.com/shadcn-ui/ui/blob/0fae3fd93ae749aca708bdfbbbeddc5d576bfb2e/apps/www/registry/default/example/cards/stats.tsx#L61 + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Line, LineChart, ResponsiveContainer } from "recharts"; +import { twColourConfig } from "@/lib/twConfig"; + +const data = [ + { + revenue: 10400, + subscription: 240, + }, + { + revenue: 14405, + subscription: 300, + }, + { + revenue: 9400, + subscription: 200, + }, + { + revenue: 8200, + subscription: 278, + }, + { + revenue: 7000, + subscription: 189, + }, + { + revenue: 9600, + subscription: 239, + }, + { + revenue: 11244, + subscription: 278, + }, + { + revenue: 26475, + subscription: 189, + }, +]; + +export function DemoRevenue() { + return ( + + + Total Revenue + + +
$15,231.89
+

+20.1% from last month

+
+ + + + + +
+
+
+ ); +} diff --git a/apps/web/components/demo-subscriptions.tsx b/apps/web/components/demo-subscriptions.tsx new file mode 100644 index 0000000..9e4d923 --- /dev/null +++ b/apps/web/components/demo-subscriptions.tsx @@ -0,0 +1,65 @@ +"use client"; +// Example data from ShadCN: https://github.com/shadcn-ui/ui/blob/0fae3fd93ae749aca708bdfbbbeddc5d576bfb2e/apps/www/registry/default/example/cards/stats.tsx#L61 + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Bar, BarChart, ResponsiveContainer } from "recharts"; +import { twColourConfig } from "@/lib/twConfig"; + +const data = [ + { + revenue: 10400, + subscription: 240, + }, + { + revenue: 14405, + subscription: 300, + }, + { + revenue: 9400, + subscription: 200, + }, + { + revenue: 8200, + subscription: 278, + }, + { + revenue: 7000, + subscription: 189, + }, + { + revenue: 9600, + subscription: 239, + }, + { + revenue: 11244, + subscription: 278, + }, + { + revenue: 26475, + subscription: 189, + }, +]; + +export function DemoSubscriptions() { + return ( + + + Subscriptions + + +
+2350
+

+180.1% from last month

+
+ + + + + +
+
+
+ ); +} diff --git a/apps/web/components/flex-wrapper.tsx b/apps/web/components/flex-wrapper.tsx new file mode 100644 index 0000000..7157fbc --- /dev/null +++ b/apps/web/components/flex-wrapper.tsx @@ -0,0 +1,74 @@ +import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; +import { ReactNode, Children } from "react"; + +const flexVariants = cva("", { + variants: { + columns: { + default: "", + "1": "flex-1 basis-full", + "2": "flex-1 basis-full sm:basis-[48%]", + "3": "flex-1 basis-full sm:basis-[48%] md:basis-[32%]", + "4": "flex-1 basis-full sm:basis-[48%] md:basis-[32%] lg:basis-[24%]", + "5": "flex-1 basis-full sm:basis-[48%] md:basis-[32%] lg:basis-[19%]", + "6": "flex-1 basis-full sm:basis-[48%] md:basis-[32%] lg:basis-[19%] xl:basis-[12%]", + }, + horizontal_position: { + start: "justify-start", + center: "justify-center text-center", + end: "justify-end", + none: null, + }, + vertical_position: { + start: "items-start", + center: "items-center text-center", + end: "items-end", + none: null, + }, + borders: { + default: "border rounded-md p-5", + none: null, + }, + }, + defaultVariants: { + columns: "default", + horizontal_position: "none", + vertical_position: "none", + borders: "none", + }, +}); + +export type FlexWrapperProps = VariantProps & { + children: ReactNode; + className?: string; +}; + +export function FlexWrapper({ + children, + className, + columns, + horizontal_position, + vertical_position, +}: FlexWrapperProps) { + return ( +
+ {Children.map(children, (child, index) => ( +
+ {/*
*/} + {child} +
+ ))} +
+ ); +} diff --git a/apps/web/components/grid-wrapper.tsx b/apps/web/components/grid-wrapper.tsx new file mode 100644 index 0000000..578bc62 --- /dev/null +++ b/apps/web/components/grid-wrapper.tsx @@ -0,0 +1,70 @@ +import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; +import { ReactNode, Children } from "react"; + +const gridVariants = cva("w-full grid gap-4 justify-between", { + variants: { + columns: { + default: + "grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 2xl:grid-cols-6", + "1": "grid-cols-1", + "2": "grid-cols-1 sm:grid-cols-2", + "3": "grid-cols-1 sm:grid-cols-2 md:grid-cols-3", + "4": "grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4", + "5": "grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5", + "6": "grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-6", + }, + horizontal_position: { + start: "justify-start", + center: "justify-center text-center", + end: "justify-end", + }, + vertical_position: { + start: "items-start", + center: "items-center text-center", + end: "items-end", + }, + borders: { + default: "border rounded-md p-5", + none: null, + }, + }, + defaultVariants: { + columns: "default", + horizontal_position: "center", + vertical_position: "center", + borders: "none", + }, +}); + +export type GridWrapperProps = VariantProps & { + children: ReactNode; + className?: string; +}; + +export function GridWrapper({ + children, + className, + columns, + horizontal_position, + vertical_position, +}: GridWrapperProps) { + const isCentered = + horizontal_position === "center" && vertical_position === "center"; + + return ( +
+ {Children.map(children, (child, index) => ( +
+ {child} +
+ ))} +
+ ); +} diff --git a/apps/web/components/icons.tsx b/apps/web/components/icons.tsx new file mode 100644 index 0000000..f7b4ad2 --- /dev/null +++ b/apps/web/components/icons.tsx @@ -0,0 +1,118 @@ +// https://lucide.dev/icons/ +// Country icons: https://www.svgrepo.com/collection/countrys-flags/ +// LinkedIn/Facebook/Twitter: https://icons8.com/icons/ + +import { + Activity, + ArrowRight, + BadgePercent, + BarChart, + Bell, + Building2, + ChevronDown, + ChevronsUpDown, + ClipboardList, + Copy, + File, + Gauge, + Globe, + Home, + LayoutTemplate, + Link, + LucideProps, + Menu, + Moon, + PanelLeftClose, + PanelLeftOpen, + Plus, + PoundSterling, + Settings, + SlidersHorizontal, + SunMedium, + User2, + Users, + Workflow, + X, +} from "lucide-react"; + +export const Icons = { + activity: Activity, + analytics: BarChart, + arrowRight: ArrowRight, + badgePercent: BadgePercent, + building: Building2, + chevronDown: ChevronDown, + chevronUpDown: ChevronsUpDown, + collaboration: Users, + copy: Copy, + file: File, + globe: Globe, + home: Home, + link: Link, + menu: Menu, + menuClose: X, + moon: Moon, + notification: Bell, + panelLeftClose: PanelLeftClose, + panelLeftOpen: PanelLeftOpen, + performance: Gauge, + plus: Plus, + poundSterling: PoundSterling, + rules: ClipboardList, + settings: Settings, + slider: SlidersHorizontal, + sun: SunMedium, + template: LayoutTemplate, + user2: User2, + workflow: Workflow, + github: (props: LucideProps) => ( + + + + ), + linkedin: (props: LucideProps) => ( + + + + ), + doubleChevron: (props: LucideProps) => ( + + + + + ), + logo: (props: LucideProps) => ( + + + + + + + + + ), +}; diff --git a/apps/web/components/layouts/dashboard/DashboardLayout.module.css b/apps/web/components/layouts/dashboard/DashboardLayout.module.css new file mode 100644 index 0000000..45032ed --- /dev/null +++ b/apps/web/components/layouts/dashboard/DashboardLayout.module.css @@ -0,0 +1,42 @@ +.dashboardWrapper { + grid-template-columns: min-content auto; + grid-template-areas: "sidebar main"; + --header-height: 5rem; + --footer-height: 2rem; + --small-spacing: 1rem; /* tw class: gap-4, m-4 etc. */ + --large-spacing: 2rem; /* tw class: gap-8, m-8 etc. */ + --header-size: calc(var(--header-height) + var(--small-spacing)); + --footer-size: calc(var(--footer-height) + var(--small-spacing)); +} + +.dashboardSidebar { + grid-area: sidebar; +} + +.dashboardMain { + @apply gap-[var(--large-spacing)]; + grid-area: main; + grid-template-rows: var(--header-height) calc( + 100vh - (var(--header-height) + var(--footer-height)) + ); + grid-template-areas: "header" "content"; +} + +.dashboardHeader { + grid-area: header; +} + +.dashboardContent { + grid-area: content; +} + +.dashboardContentWrapper { + @apply gap-[var(--small-spacing)]; + min-height: calc( + 100vh - + ( + var(--header-size) + var(--footer-size) + var(--small-spacing) + + var(--small-spacing) + ) + ); +} diff --git a/apps/web/components/layouts/dashboard/DashboardLayout.tsx b/apps/web/components/layouts/dashboard/DashboardLayout.tsx new file mode 100644 index 0000000..7a579db --- /dev/null +++ b/apps/web/components/layouts/dashboard/DashboardLayout.tsx @@ -0,0 +1,35 @@ +import styles from "./DashboardLayout.module.css"; +import { Footer, Header, Sidebar } from "./layout-components"; + +type DashboardLayoutProps = { + children: React.ReactNode; +}; + +export default function DashboardLayout({ children }: DashboardLayoutProps) { + return ( +
+
+ +
+
+
+
+
+
+
+ {children} +
+
+
+
+
+
+
+ ); +} diff --git a/apps/web/components/layouts/dashboard/index.ts b/apps/web/components/layouts/dashboard/index.ts new file mode 100644 index 0000000..d83db63 --- /dev/null +++ b/apps/web/components/layouts/dashboard/index.ts @@ -0,0 +1 @@ +export { default as DashboardLayout } from "../dashboard/DashboardLayout"; diff --git a/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.module.css b/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.module.css new file mode 100644 index 0000000..b856cbd --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.module.css @@ -0,0 +1,4 @@ +/* Vars here are coming from /DashboardLayout.module.css */ +.dashboardFooter { + @apply mt-[var(--small-spacing)] min-h-[var(--footer-height)]; +} diff --git a/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.tsx b/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.tsx new file mode 100644 index 0000000..ae38321 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Footer/Footer.tsx @@ -0,0 +1,17 @@ +import styles from "./Footer.module.css"; + +const Footer = () => { + return ( +
+
+
+
+ Copyright© Next-Fast-Turbo. All rights reserved. +
+
+
+
+ ); +}; + +export default Footer; diff --git a/apps/web/components/layouts/dashboard/layout-components/Footer/index.ts b/apps/web/components/layouts/dashboard/layout-components/Footer/index.ts new file mode 100644 index 0000000..3738288 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Footer/index.ts @@ -0,0 +1 @@ +export { default } from "./Footer"; diff --git a/apps/web/components/layouts/dashboard/layout-components/Header/Header.tsx b/apps/web/components/layouts/dashboard/layout-components/Header/Header.tsx new file mode 100644 index 0000000..c953d53 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Header/Header.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { usePathname } from "next/navigation"; +import { navConfig } from "@/lib/config"; +import { ModeToggle } from "@/components/theme/mode-toggle"; +import { SidebarMobile } from "../../layout-components"; + +const Header = () => { + const pathName = usePathname(); + const pageTitle = navConfig.navLinks.find((elem) => { + if (elem.href === pathName) { + return elem.pageTitle; + } + }); + + return ( +
+
+ {pageTitle?.pageTitle} +
+
+
+ +
+
+ +
+
+
+ ); +}; + +export default Header; diff --git a/apps/web/components/layouts/dashboard/layout-components/Header/index.ts b/apps/web/components/layouts/dashboard/layout-components/Header/index.ts new file mode 100644 index 0000000..2764567 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Header/index.ts @@ -0,0 +1 @@ +export { default } from "./Header"; diff --git a/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLink.tsx b/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLink.tsx new file mode 100644 index 0000000..4c09425 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLink.tsx @@ -0,0 +1,82 @@ +"use client"; + +import Link from "next/link"; +import { AnimatePresence, motion } from "framer-motion"; + +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +export default function LinkComponent(...props: any) { + const activeLink = props[0].activeLink; + const collapsed = props[0].collapsed; + const animationDuration = props[0].animationDuration; + + return ( + + {collapsed ? ( + + + +
+
+ {props[0].icon} +
+ + + {props[0].label} + + +
+
+ +

{props[0].label}

+
+
+
+ ) : ( +
+
{props[0].icon}
+ + + {props[0].label} + + +
+ )} + + ); +} diff --git a/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLinks.tsx b/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLinks.tsx new file mode 100644 index 0000000..6cd08ca --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Sidebar/NavLinks.tsx @@ -0,0 +1,67 @@ +import { FC } from "react"; +import { usePathname } from "next/navigation"; +import LinkComponent from "./NavLink"; +import { navConfig } from "@/lib/config/"; + +type NavLinksProps = { + collapsed: boolean; + animationDuration: number; +}; + +const NavLinks: FC = ({ collapsed, animationDuration }) => { + const pathName = usePathname(); + + return ( +
+ + + +
+ ); +}; + +export default NavLinks; diff --git a/apps/web/components/layouts/dashboard/layout-components/Sidebar/Sidebar.tsx b/apps/web/components/layouts/dashboard/layout-components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..d1e55d0 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Sidebar/Sidebar.tsx @@ -0,0 +1,84 @@ +"use client"; +import { useEffect } from "react"; +import { useState } from "react"; +import Link from "next/link"; +import { Icons } from "@/components/icons"; +import { motion } from "framer-motion"; +import NavLinks from "./NavLinks"; + +const Sidebar = () => { + const [collapsed, setCollapsed] = useState(false); + const animationDuration = 0.4; + const sideBarWidth = "250px"; + + // Load collapsed state from localStorage on component mount + useEffect(() => { + const collapsedState = localStorage.getItem("sidebarCollapsed"); + if (collapsedState !== null) { + setCollapsed(collapsedState === "true" ? true : false); + } + }, []); + + const handleClose = () => { + localStorage.setItem("sidebarCollapsed", (!collapsed).toString()); + setCollapsed(!collapsed); + }; + + return ( + +
+ + +
+ + + + Next-Fast-Turbo +
+
+ + + {collapsed ? ( + + ) : ( + + )} +
+
+ +
+
+ ); +}; + +export default Sidebar; diff --git a/apps/web/components/layouts/dashboard/layout-components/Sidebar/SidebarMobile.tsx b/apps/web/components/layouts/dashboard/layout-components/Sidebar/SidebarMobile.tsx new file mode 100644 index 0000000..b0b0edb --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Sidebar/SidebarMobile.tsx @@ -0,0 +1,58 @@ +"use client"; +import { useRef } from "react"; +import { useState } from "react"; +import { Squash as Hamburger } from "hamburger-react"; +import { useClickAway } from "react-use"; +import { navConfig } from "@/lib/config/"; + +import { AnimatePresence, motion } from "framer-motion"; +import Link from "next/link"; +import { Separator } from "@/components/ui/separator"; + +const SidebarMobile = () => { + const [isOpen, setOpen] = useState(false); + const ref = useRef(null); + + useClickAway(ref, () => setOpen(false)); + + return ( +
+ + + {isOpen && ( + +
    + {navConfig.navLinks.map((link, index) => { + return ( + + {link.pageTitle} + + + ); + })} +
+
+ )} +
+
+ ); +}; + +export default SidebarMobile; diff --git a/apps/web/components/layouts/dashboard/layout-components/Sidebar/index.ts b/apps/web/components/layouts/dashboard/layout-components/Sidebar/index.ts new file mode 100644 index 0000000..7334011 --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/Sidebar/index.ts @@ -0,0 +1,2 @@ +export { default as Sidebar } from "./Sidebar"; +export { default as SidebarMobile } from "./SidebarMobile"; diff --git a/apps/web/components/layouts/dashboard/layout-components/index.ts b/apps/web/components/layouts/dashboard/layout-components/index.ts new file mode 100644 index 0000000..7ac056f --- /dev/null +++ b/apps/web/components/layouts/dashboard/layout-components/index.ts @@ -0,0 +1,4 @@ +export { Sidebar } from "./Sidebar"; +export { SidebarMobile } from "./Sidebar"; +export { default as Header } from "./Header"; +export { default as Footer } from "./Footer"; diff --git a/apps/web/components/search-users.tsx b/apps/web/components/search-users.tsx new file mode 100644 index 0000000..08a73b7 --- /dev/null +++ b/apps/web/components/search-users.tsx @@ -0,0 +1,209 @@ +"use client"; +import * as React from "react"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Skeleton } from "./ui/skeleton"; +import { Input } from "@/components/ui/input"; +import { UsersService, UserSearchResults } from "@/lib/api/client"; + +const searchOnFields = ["id", "email", "forename", "surname"]; + +const FormSchema = z.object({ + keyword: z.string().optional(), + searchOn: z.enum(["id", "email", "forename", "surname"]).optional(), + searchResults: z + .string() + .min(1, { + message: "Must return at least 1 result", + }) + .optional(), +}); + +export default function SearchUsers() { + const [searchResults, setSearchResults] = React.useState({ + results: [], + }); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(false); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + keyword: ".com", + searchOn: "email", + searchResults: "10", + }, + }); + + const onSubmit = async (data: z.infer) => { + console.log(data); + try { + setLoading(true); // set loading state + setError(null); // clear error state if it exists + const maxResults = data.searchResults ? parseInt(data.searchResults) : 10; + const response = await UsersService.usersSearchUsers({ + keyword: data.keyword, + searchOn: data.searchOn, + maxResults: maxResults, + }); + setLoading(false); + console.log(response); + setSearchResults(response); + setError(null); + } catch (error) { + console.log("Error received", error); + setLoading(false); + setSearchResults({ results: [] }); + setError(error); + } + }; + + return ( +
+
+ + + + FastAPI data + + Data coming from FastAPI backend + + + + +
+ ( + + Keyword + + + + The keyword to search. + + + )} + /> + ( + + Max results + + + + + Set maximum number of results to return. + + + + )} + /> + ( + + Search field + + + {searchOnFields.map((item) => ( + + + + + + {item.charAt(0).toUpperCase() + item.slice(1)} + + + ))} + + + The field to search on. + + )} + /> +
+
+ +
+ + +
+
+ +
+ + + {loading ? ( + + ) : null} + + {searchResults.results.length >= 1 ? ( + // Render the results if searchResults is set +
+ {/* Replace this with your code to render the results */} +
{JSON.stringify(searchResults, null, 2)}
+
+ ) : null} + + {/* This can be handled better to understand what type of error is occurring rather than just a blanket handler */} + {error ? ( +
+ Couldn't find any results that match your criteria. Please try again. +
+ ) : null} +
+ ); +} diff --git a/apps/web/components/tailwind-indicator.tsx b/apps/web/components/tailwind-indicator.tsx new file mode 100644 index 0000000..3d1b062 --- /dev/null +++ b/apps/web/components/tailwind-indicator.tsx @@ -0,0 +1,18 @@ +// https://github.com/shadcn-ui/taxonomy/blob/main/components/tailwind-indicator.tsx#L1 + +export function TailwindIndicator() { + if (process.env.NODE_ENV === "production") return null; + + return ( +
+
xs
+
+ sm +
+
md
+
lg
+
xl
+
2xl
+
+ ); +} diff --git a/apps/web/components/theme/mode-toggle.tsx b/apps/web/components/theme/mode-toggle.tsx new file mode 100644 index 0000000..adf561c --- /dev/null +++ b/apps/web/components/theme/mode-toggle.tsx @@ -0,0 +1,25 @@ +"use client"; + +import * as React from "react"; +import { MoonIcon, SunIcon } from "@radix-ui/react-icons"; +import { useTheme } from "next-themes"; + +import { Button } from "@/components/ui/button"; + +export function ModeToggle() { + const { setTheme, resolvedTheme } = useTheme(); + + const handleSubmit = () => { + setTheme(resolvedTheme === "dark" ? "light" : "dark"); + }; + + return ( + <> + + + ); +} diff --git a/apps/web/components/theme/theme-provider.tsx b/apps/web/components/theme/theme-provider.tsx new file mode 100644 index 0000000..b0ff266 --- /dev/null +++ b/apps/web/components/theme/theme-provider.tsx @@ -0,0 +1,9 @@ +"use client"; + +import * as React from "react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { type ThemeProviderProps } from "next-themes/dist/types"; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children}; +} diff --git a/apps/web/components/theme/theme.css b/apps/web/components/theme/theme.css new file mode 100644 index 0000000..fc09588 --- /dev/null +++ b/apps/web/components/theme/theme.css @@ -0,0 +1,48 @@ +@tailwind base; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 224 71.4% 4.1%; + --card: 0 0% 100%; + --card-foreground: 224 71.4% 4.1%; + --popover: 0 0% 100%; + --popover-foreground: 224 71.4% 4.1%; + --primary: 262.1 83.3% 57.8%; + --primary-foreground: 210 20% 98%; + --secondary: 220 14.3% 95.9%; + --secondary-foreground: 220.9 39.3% 11%; + --muted: 220 14.3% 95.9%; + --muted-foreground: 220 8.9% 46.1%; + --accent: 220 14.3% 95.9%; + --accent-foreground: 220.9 39.3% 11%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 20% 98%; + --border: 220 13% 91%; + --input: 220 13% 91%; + --ring: 262.1 83.3% 57.8%; + --radius: 0.5rem; + } + + .dark { + --background: 224 71.4% 4.1%; + --foreground: 210 20% 98%; + --card: 224 71.4% 4.1%; + --card-foreground: 210 20% 98%; + --popover: 224 71.4% 4.1%; + --popover-foreground: 210 20% 98%; + --primary: 263.4 70% 50.4%; + --primary-foreground: 210 20% 98%; + --secondary: 215 27.9% 16.9%; + --secondary-foreground: 210 20% 98%; + --muted: 215 27.9% 16.9%; + --muted-foreground: 217.9 10.6% 64.9%; + --accent: 215 27.9% 16.9%; + --accent-foreground: 210 20% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 20% 98%; + --border: 215 27.9% 16.9%; + --input: 215 27.9% 16.9%; + --ring: 263.4 70% 50.4%; + } +} diff --git a/apps/web/components/typography.tsx b/apps/web/components/typography.tsx new file mode 100644 index 0000000..c65c42d --- /dev/null +++ b/apps/web/components/typography.tsx @@ -0,0 +1,82 @@ +// WIP component. Will flesh out more as we develop + +import * as React from "react"; +import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; + +const typographyVariants = cva("m-0 self-center p-0", { + variants: { + variant: { + h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl", + h2: "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0", + h3: "scroll-m-20 text-2xl font-semibold tracking-tight", + h4: "scroll-m-20 text-xl font-semibold tracking-tight", + p: "leading-7 [&:not(:first-child)]:mt-6", + code: "relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold", + lead: "text-xl text-muted-foreground", + }, + size: { + default: "text-lg", + sm: "text-sm", + lg: "text-xl", + }, + colour: { + default: "text-foreground", + muted: "text-muted-foreground", + accent: "text-accent", + inverted: "text-background", + }, + }, + defaultVariants: { + variant: "p", + size: "default", + colour: "default", + }, +}); + +export interface TypographyProps + extends React.HTMLAttributes, + VariantProps { + children: React.ReactNode; +} + +const Typography = ({ + variant, + size, + colour, + className, + children, +}: TypographyProps) => { + let HeadingComponent: React.ElementType = "div"; + + switch (variant) { + case "h1": + HeadingComponent = "h1"; + break; + case "h2": + HeadingComponent = "h2"; + break; + case "h3": + HeadingComponent = "h3"; + break; + case "h4": + HeadingComponent = "h4"; + break; + case "p": + HeadingComponent = "p"; + break; + case "code": + HeadingComponent = "code"; + break; + } + + return ( + + {children} + + ); +}; + +export { Typography, typographyVariants as buttonVariants }; diff --git a/apps/web/components/ui/button.tsx b/apps/web/components/ui/button.tsx new file mode 100644 index 0000000..57c9fe4 --- /dev/null +++ b/apps/web/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/apps/web/components/ui/card.tsx b/apps/web/components/ui/card.tsx new file mode 100644 index 0000000..dc3b01d --- /dev/null +++ b/apps/web/components/ui/card.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/apps/web/components/ui/dropdown-menu.tsx b/apps/web/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..3a0c7fe --- /dev/null +++ b/apps/web/components/ui/dropdown-menu.tsx @@ -0,0 +1,200 @@ +"use client"; + +import * as React from "react"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { Check, ChevronRight, Circle } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)); +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/apps/web/components/ui/form.tsx b/apps/web/components/ui/form.tsx new file mode 100644 index 0000000..497718a --- /dev/null +++ b/apps/web/components/ui/form.tsx @@ -0,0 +1,177 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +