diff --git a/packages/builder/.gitignore b/packages/builder/.gitignore new file mode 100644 index 000000000..328f7f0f3 --- /dev/null +++ b/packages/builder/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +node_modules +node_modules_win +package-lock.json +yarn.lock +release/ +dist/ \ No newline at end of file diff --git a/packages/builder/AUTHORS.md b/packages/builder/AUTHORS.md new file mode 100644 index 000000000..3411a7267 --- /dev/null +++ b/packages/builder/AUTHORS.md @@ -0,0 +1,4 @@ +Contributors +=== + +* Michael Shanks - [@mikebudi](https://github.com/mikebudi) \ No newline at end of file diff --git a/packages/builder/CONTRIBUTING.md b/packages/builder/CONTRIBUTING.md new file mode 100644 index 000000000..24bd68258 --- /dev/null +++ b/packages/builder/CONTRIBUTING.md @@ -0,0 +1,22 @@ +### Contributing to budibase-core + +* The contributors are listed in [AUTHORS.md](https://github.com/budibase/builder/blob/master/AUTHORS.md) (add yourself). + +* This project uses a modified version of the MPLv2 license, see [LICENSE](https://github.com/budibase/builder/blob/master/LICENSE.md). + +* We use the [C4 (Collective Code Construction Contract)](https://rfc.zeromq.org/spec:42/C4/) process for contributions. +Please read this if you are unfamiliar with it. + +* Please maintain the existing code style. + +* Please try to keep your commits small and focussed. + +* If the project diverges from your branch, please rebase instead of merging. This makes the commit graph easier to read. + +#### p.S... + +I am using contribution guidelines from the fantastic [ZeroMQ](https://github.com/zeromq) community. If you are interested why, it's because I believe in the ethos laid out by this community, and written about in depth in the book ["Social Architecture"](https://www.amazon.com/Social-Architecture-Building-line-Communities/dp/1533112452) by Pieter Hintjens. + +I am very much open to evolving this to suit our needs. + +Love from [Mike](https://github.com/mikebudi). diff --git a/packages/builder/LICENSE.md b/packages/builder/LICENSE.md new file mode 100644 index 000000000..0ad25db4b --- /dev/null +++ b/packages/builder/LICENSE.md @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/packages/builder/README.md b/packages/builder/README.md new file mode 100644 index 000000000..7256dfd00 --- /dev/null +++ b/packages/builder/README.md @@ -0,0 +1,6 @@ +## Get Started + +`npm install` + +Webpack dev server (port 8080): `npm run dev` +Build `npm run build` diff --git a/packages/builder/package.json b/packages/builder/package.json new file mode 100644 index 000000000..7d681ab64 --- /dev/null +++ b/packages/builder/package.json @@ -0,0 +1,40 @@ +{ + "name": "budibase-builder", + "version": "0.0.1", + "license": "AGPL-3.0", + "scripts": { + "build": "rollup -c", + "start": "rollup -c -w" + }, + "dependencies": { + "@nx-js/compiler-util": "^2.0.0", + "budibase-core": "file:../core/dist", + "date-fns": "^1.29.0", + "feather-icons": "^4.21.0", + "flatpickr": "^4.5.7", + "lodash": "^4.17.11", + "lunr": "^2.3.5", + "safe-buffer": "^5.1.2", + "shortid": "^2.2.8", + "string_decoder": "^1.2.0", + "uikit": "^3.1.5" + }, + "devDependencies": { + "browser-sync": "^2.26.7", + "http-proxy-middleware": "^0.19.1", + "npm-run-all": "^4.1.5", + "rollup": "^1.12.0", + "rollup-plugin-alias": "^1.5.2", + "rollup-plugin-browsersync": "^1.0.0", + "rollup-plugin-commonjs": "^10.0.0", + "rollup-plugin-copy": "^3.0.0", + "rollup-plugin-livereload": "^1.0.0", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-node-globals": "^1.4.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-svelte": "^5.0.3", + "rollup-plugin-terser": "^4.0.4", + "rollup-plugin-url": "^2.2.2", + "svelte": "^3.0.0" + } +} diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js new file mode 100644 index 000000000..6f7eddcd1 --- /dev/null +++ b/packages/builder/rollup.config.js @@ -0,0 +1,113 @@ +import svelte from 'rollup-plugin-svelte'; +import resolve from 'rollup-plugin-node-resolve'; +import commonjs from 'rollup-plugin-commonjs'; +import url from 'rollup-plugin-url'; +import livereload from 'rollup-plugin-livereload'; +import { terser } from 'rollup-plugin-terser'; +import builtins from 'rollup-plugin-node-builtins'; +import nodeglobals from 'rollup-plugin-node-globals'; +import copy from 'rollup-plugin-copy'; +import browsersync from "rollup-plugin-browsersync"; +import proxy from "http-proxy-middleware"; + +const apiProxy = proxy('/api', { + target: 'http://localhost:4001/_builder', + logLevel: "debug", + changeOrigin: true, + cookieDomainRewrite: true, + onProxyReq(proxyReq) { + if (proxyReq.getHeader("origin")) { + proxyReq.setHeader("origin", target) + } + } +}); + +const production = !process.env.ROLLUP_WATCH; + +const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", +"take", "first", "intersection", "mapValues", "isNull", "has", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", +"keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", +"differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign"]; + +const lodash_exports = ["toNumber", "flow", "isArray", "join", "replace", "trim", "dropRight", "takeRight", "head", "isUndefined", "isNull", "isNaN", "reduce", "isEmpty", +"constant", "tail", "includes", "startsWith", "findIndex", "isInteger", "isDate", "isString", "split", "clone", "keys", "isFunction", "merge", "has", "isBoolean", "isNumber", +"isObjectLike", "assign", "some", "each", "find", "orderBy", "union", "cloneDeep"]; + +const writeoptions = {dest: "output/output.js"}; + +export default { + input: 'src/main.js', + output: { + sourcemap: true, + format: 'iife', + name: 'app', + file: 'dist/bundle.js' + }, + plugins: [ + copy({ + targets: [ + { src: 'src/index.html', dest: 'dist' }, + { src: 'src/favicon.png', dest: 'dist' } + ] + }), + + svelte({ + // enable run-time checks when not in production + dev: !production, + include: 'src/**/*.svelte', + // we'll extract any component CSS out into + // a separate file — better for performance + css: css => { + css.write('dist/bundle.css'); + } + }), + + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration — + // consult the documentation for details: + // https://github.com/rollup/rollup-plugin-commonjs + resolve({ + browser: true, + dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/') + + }), + commonjs({ + namedExports: { + "lodash/fp": lodash_fp_exports, + "lodash":lodash_exports, + "shortid": ["generate"] + }, + include: /node_modules/ + }), + url({ + limit: 0, + include: ["**/*.woff2", "**/*.png"], + fileName: "[dirname][name][extname]", + emitFiles: true + }), + url({ + limit: 0, + include: ["**/*.css"], + fileName: "[name][extname]", + emitFiles: true + }), + builtins(), + nodeglobals(), + + // Watch the `dist` directory and refresh the + // browser on changes when not in production + !production && livereload('dist'), + !production && browsersync({ + server: 'dist', + middleware: [apiProxy] + }), + + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser() + ], + watch: { + clearScreen: false + } +}; diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte new file mode 100644 index 000000000..cc1beff9b --- /dev/null +++ b/packages/builder/src/App.svelte @@ -0,0 +1,38 @@ + + +
+ + {#await init} + +

loading

+ + {:then result} + {#if $database.hasAppPackage} + + {/if} + + {#if !$database.hasAppPackage} + + {/if} + + {:catch err} +

{err}

+ {/await} +
+ + \ No newline at end of file diff --git a/packages/builder/src/NoPackage.svelte b/packages/builder/src/NoPackage.svelte new file mode 100644 index 000000000..eb0cade2e --- /dev/null +++ b/packages/builder/src/NoPackage.svelte @@ -0,0 +1,55 @@ + + +
+
+ +
+ +
+

What would you like not to do?

+ + +
+
+
+
+ + \ No newline at end of file diff --git a/packages/builder/src/PackageRoot.svelte b/packages/builder/src/PackageRoot.svelte new file mode 100644 index 000000000..adf483381 --- /dev/null +++ b/packages/builder/src/PackageRoot.svelte @@ -0,0 +1,49 @@ + + +
+
+ + + + \ No newline at end of file diff --git a/packages/builder/src/accessLevels/AccessLevelView.svelte b/packages/builder/src/accessLevels/AccessLevelView.svelte new file mode 100644 index 000000000..24e962cc3 --- /dev/null +++ b/packages/builder/src/accessLevels/AccessLevelView.svelte @@ -0,0 +1,96 @@ + + +
+ + + + + + {#each permissionMatrix as permission} +
+ +
+ {/each} + + + + + + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/accessLevels/AccessLevelsRoot.svelte b/packages/builder/src/accessLevels/AccessLevelsRoot.svelte new file mode 100644 index 000000000..b6d46f32e --- /dev/null +++ b/packages/builder/src/accessLevels/AccessLevelsRoot.svelte @@ -0,0 +1,116 @@ + + +
+ + + + + +{#if $database.accessLevels} + + + + + + + + + + {#each $database.accessLevels as level} + + + + + + {/each} + +
NamePermissions
{level.name}{getPermissionsString(level.permissions)} + onLevelEdit(level)}>{@html getIcon("edit")} + onLevelDelete(level)}>{@html getIcon("trash")} +
+{:else} +(no actions added) +{/if} + + + + {#if isEditing} + + {/if} + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/actionsAndTriggers/ActionView.svelte b/packages/builder/src/actionsAndTriggers/ActionView.svelte new file mode 100644 index 000000000..8f06b4257 --- /dev/null +++ b/packages/builder/src/actionsAndTriggers/ActionView.svelte @@ -0,0 +1,115 @@ + + +
+ + + + + + + + +
+ +
+ + + +
+
+ {#each initialOptions as option} + {option.key} : {option.value} removeOption(option)}>{@html getIcon("trash-2")} + {/each} +
+
+ + + + + + + +
+ + + \ No newline at end of file diff --git a/packages/builder/src/actionsAndTriggers/Actions.svelte b/packages/builder/src/actionsAndTriggers/Actions.svelte new file mode 100644 index 000000000..91c5cd57e --- /dev/null +++ b/packages/builder/src/actionsAndTriggers/Actions.svelte @@ -0,0 +1,95 @@ + + +

Actions

+ +{#if $database.actions} + + + + + + + + + + + + {#each $database.actions as action} + + + + + + + + {/each} + +
DescriptionBehaviour SourceBehaviour NameDefault Options
{action.name}{action.behaviourSource}{action.behaviourName}{@html getDefaultOptionsHtml(action.initialOptions)} + onActionEdit(action)}>{@html getIcon("edit")} + onActionDelete(action)}>{@html getIcon("trash")} +
+{:else} +(no actions added) +{/if} + + + + {#if isEditing} + + {/if} + + + + \ No newline at end of file diff --git a/packages/builder/src/actionsAndTriggers/ActionsAndTriggersRoot.svelte b/packages/builder/src/actionsAndTriggers/ActionsAndTriggersRoot.svelte new file mode 100644 index 000000000..aeae9f398 --- /dev/null +++ b/packages/builder/src/actionsAndTriggers/ActionsAndTriggersRoot.svelte @@ -0,0 +1,115 @@ + + +
+ + + + + + + + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/actionsAndTriggers/TriggerView.svelte b/packages/builder/src/actionsAndTriggers/TriggerView.svelte new file mode 100644 index 000000000..58520f459 --- /dev/null +++ b/packages/builder/src/actionsAndTriggers/TriggerView.svelte @@ -0,0 +1,64 @@ + + +
+ + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/actionsAndTriggers/Triggers.svelte b/packages/builder/src/actionsAndTriggers/Triggers.svelte new file mode 100644 index 000000000..bccd45ee1 --- /dev/null +++ b/packages/builder/src/actionsAndTriggers/Triggers.svelte @@ -0,0 +1,84 @@ + + +

Triggers

+ +{#if $database.triggers} + + + + + + + + + + + + {#each $database.triggers as trigger} + + + + + + + + {/each} + +
EventActionConditionCreate Options
{trigger.eventName}{trigger.actionName}{trigger.condition}{trigger.optionsCreator} + onTriggerEdit(trigger)}>{@html getIcon("edit")} + onTriggerDelete(trigger)}>{@html getIcon("trash")} +
+{:else} +(no triggers added) +{/if} + + + + {#if isEditing} + + {/if} + + + \ No newline at end of file diff --git a/packages/builder/src/assets/budibase-logo-only.png b/packages/builder/src/assets/budibase-logo-only.png new file mode 100644 index 000000000..652444e5b Binary files /dev/null and b/packages/builder/src/assets/budibase-logo-only.png differ diff --git a/packages/builder/src/assets/budibase-logo-white.png b/packages/builder/src/assets/budibase-logo-white.png new file mode 100644 index 000000000..da8440c09 Binary files /dev/null and b/packages/builder/src/assets/budibase-logo-white.png differ diff --git a/packages/builder/src/assets/budibase-logo.png b/packages/builder/src/assets/budibase-logo.png new file mode 100644 index 000000000..1d6fc3d1c Binary files /dev/null and b/packages/builder/src/assets/budibase-logo.png differ diff --git a/packages/builder/src/assets/lato-latin-ext.woff2 b/packages/builder/src/assets/lato-latin-ext.woff2 new file mode 100644 index 000000000..0a0ee5eae Binary files /dev/null and b/packages/builder/src/assets/lato-latin-ext.woff2 differ diff --git a/packages/builder/src/assets/lato-latin.woff2 b/packages/builder/src/assets/lato-latin.woff2 new file mode 100644 index 000000000..6904b6649 Binary files /dev/null and b/packages/builder/src/assets/lato-latin.woff2 differ diff --git a/packages/builder/src/builderStore/createPackage.js b/packages/builder/src/builderStore/createPackage.js new file mode 100644 index 000000000..d5fe2bd46 --- /dev/null +++ b/packages/builder/src/builderStore/createPackage.js @@ -0,0 +1,663 @@ +import {createNewHeirarchy} from "../common/core"; + +export const createPackage = (packageInfo, database) => { + packageInfo.createNewPackage(""); + const root = createNewHeirarchy(); + database.importAppDefinition({ + hierarchy:root, + actions:[], + triggers:[], + accessLevels: [], + accessLevelsVersion: 0 + }); +}; + +const testroot = { + "name": "root", + "type": "root", + "children": [ + { + "name": "settings", + "type": "record", + "fields": [ + { + "name": "appName", + "type": "string", + "typeOptions": { + "maxLength": null, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "appName", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [], + "validationRules": [], + "nodeId": 1, + "indexes": [], + "allidsShardFactor": 64, + "collectionName": "", + "isSingle": true + }, + { + "name": "customer", + "type": "record", + "fields": [ + { + "name": "surname", + "type": "string", + "typeOptions": { + "maxLength": null, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "surname", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "isalive", + "type": "bool", + "typeOptions": { + "allowNulls": true + }, + "label": "isalive", + "getInitialValue": "true", + "getUndefinedValue": "default" + }, + { + "name": "createddate", + "type": "datetime", + "typeOptions": { + "maxValue": "+275760-09-13T00:00:00.000Z", + "minValue": "-271821-04-20T00:00:00.000Z" + }, + "label": "createddate", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "age", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 0 + }, + "label": "age", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "profilepic", + "type": "file", + "typeOptions": {}, + "label": "profilepic", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "partner", + "type": "reference", + "typeOptions": { + "indexNodeKey": "/partnersReference", + "displayValue": "name", + "reverseIndexNodeKeys": [ + "/partners/4-{id}/partnerCustomers" + ] + }, + "label": "partner", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "referredBy", + "type": "reference", + "typeOptions": { + "indexNodeKey": "/customer_index", + "displayValue": "surname", + "reverseIndexNodeKeys": [ + "/customers/2-{id}/referredToCustomers" + ] + }, + "label": "referredBy", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [ + { + "name": "invoice", + "type": "record", + "fields": [ + { + "name": "totalIncVat", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 2 + }, + "label": "totalIncVat", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "createdDate", + "type": "datetime", + "typeOptions": { + "maxValue": "+275760-09-13T00:00:00.000Z", + "minValue": "-271821-04-20T00:00:00.000Z" + }, + "label": "createdDate", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "paidAmount", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 0 + }, + "label": "paidAmount", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "invoiceType", + "type": "string", + "typeOptions": { + "maxLength": null, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "invoiceType", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "isWrittenOff", + "type": "bool", + "typeOptions": { + "allowNulls": true + }, + "label": "isWrittenOff", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "customer", + "type": "reference", + "typeOptions": { + "indexNodeKey": "/customersReference", + "reverseIndexNodeKeys": [ + "/customers/2-{id}/invoice_index" + ], + "displayValue": "name" + }, + "label": "customer", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [ + { + "name": "charge", + "type": "record", + "fields": [ + { + "name": "amount", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 0 + }, + "label": "amount", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "partnerInvoice", + "type": "reference", + "typeOptions": { + "reverseIndexNodeKeys": [ + "/partners/4-{id}/invoices/6-{id}/partnerCharges" + ], + "displayValue": "createdDate", + "indexNodeKey": "/partners/4-{id}/partnerInvoices_index" + }, + "label": "partnerInvoice", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [], + "validationRules": [], + "nodeId": 10, + "indexes": [], + "allidsShardFactor": 1, + "collectionName": "charges", + "isSingle": false + } + ], + "validationRules": [], + "nodeId": 8, + "indexes": [ + { + "name": "charge_index", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 10 + ], + "nodeId": 11 + } + ], + "allidsShardFactor": 1, + "collectionName": "invoices", + "isSingle": false + } + ], + "validationRules": [], + "nodeId": 2, + "indexes": [ + { + "name": "invoice_index", + "type": "index", + "map": "return {createdDate: record.createdDate, totalIncVat: record.totalIncVat};", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 8 + ], + "nodeId": 9 + }, + { + "name": "referredToCustomers", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "reference", + "getShardName": "return !record.surname ? 'null' : record.surname.substring(0,1);", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 14 + }, + { + "name": "invoicesByOutstanding", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "ancestor", + "getShardName": "return (record.totalIncVat > record.paidAmount ? 'outstanding' : 'paid');", + "getSortKey": "record.id", + "aggregateGroups": [ + { + "name": "all_invoices_by_type", + "type": "aggregateGroup", + "groupBy": "return record.invoiceType", + "aggregates": [ + { + "name": "totalIncVat", + "aggregatedValue": "return record.totalIncVat" + } + ], + "condition": "", + "nodeId": 26 + } + ], + "allowedRecordNodeIds": [ + 6, + 8 + ], + "nodeId": 25 + } + ], + "allidsShardFactor": 64, + "collectionName": "customers", + "isSingle": false + }, + { + "name": "partner", + "type": "record", + "fields": [ + { + "name": "businessName", + "type": "string", + "typeOptions": { + "maxLength": null, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "businessName", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [ + { + "name": "invoice", + "type": "record", + "fields": [ + { + "name": "totalIncVat", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 2 + }, + "label": "totalIncVat", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "createdDate", + "type": "datetime", + "typeOptions": { + "maxValue": "+275760-09-13T00:00:00.000Z", + "minValue": "-271821-04-20T00:00:00.000Z" + }, + "label": "createdDate", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "paidAmount", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 0 + }, + "label": "paidAmount", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [], + "validationRules": [], + "nodeId": 6, + "indexes": [ + { + "name": "partnerCharges", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "reference", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + { + "name": "charge", + "type": "record", + "fields": [ + { + "name": "amount", + "type": "number", + "typeOptions": { + "maxValue": 9007199254740991, + "minValue": -9007199254740991, + "decimalPlaces": 0 + }, + "label": "amount", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, + { + "name": "partnerInvoice", + "type": "reference", + "typeOptions": { + "reverseIndexNodeKeys": [ + "/partners/4-{id}/invoices/6-{id}/partnerCharges" + ], + "displayValue": "createdDate", + "indexNodeKey": "/partners/4-{id}/partnerInvoices_index" + }, + "label": "partnerInvoice", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [], + "validationRules": [], + "nodeId": 10, + "indexes": [], + "allidsShardFactor": 1, + "collectionName": "charges", + "isSingle": false + } + ], + "nodeId": 15 + } + ], + "allidsShardFactor": 1, + "collectionName": "invoices", + "isSingle": false + } + ], + "validationRules": [], + "nodeId": 4, + "indexes": [ + { + "name": "partnerInvoices_index", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 6 + ], + "nodeId": 7 + }, + { + "name": "partnerCustomers", + "type": "index", + "map": "return {...record};", + "filter": "record.isalive === true", + "indexType": "reference", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 13 + } + ], + "allidsShardFactor": 64, + "collectionName": "partners", + "isSingle": false + } + ], + "pathMaps": [], + "indexes": [ + { + "name": "customer_index", + "type": "index", + "map": "return record;", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [ + { + "name": "Customers Summary", + "type": "aggregateGroup", + "groupBy": "", + "aggregates": [ + { + "name": "all customers - age breakdown", + "aggregatedValue": "return record.age" + } + ], + "condition": "", + "nodeId": 24 + } + ], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 3 + }, + { + "name": "partner_index", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 4 + ], + "nodeId": 5 + }, + { + "name": "partnersReference", + "type": "index", + "map": "return {name:record.businessName};", + "filter": "", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 4 + ], + "nodeId": 12 + }, + { + "name": "customersReference", + "type": "index", + "map": "return {name:record.surname}", + "filter": "record.isalive === true", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 16 + }, + { + "name": "deceased", + "type": "index", + "map": "return {surname: record.surname, age:record.age};", + "filter": "record.isalive === false", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 17 + }, + { + "name": "customer_invoices", + "type": "index", + "map": "return record;", + "filter": "record.type === 'invoice'", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 8 + ], + "nodeId": 18 + }, + { + "name": "Outstanding Invoices", + "type": "index", + "map": "return {...record};", + "filter": "record.type === 'invoice' && record.paidAmount < record.totalIncVat", + "indexType": "ancestor", + "getShardName": "", + "getSortKey": "record.id", + "aggregateGroups": [ + { + "name": "all_invoices", + "type": "aggregateGroup", + "groupBy": "", + "aggregates": [], + "condition": "", + "nodeId": 20 + }, + { + "name": "all_invoices_by_type", + "type": "aggregateGroup", + "groupBy": "return record.invoiceType", + "aggregates": [ + { + "name": "totalIncVat", + "aggregatedValue": "return record.totalIncVat" + }, + { + "name": "paidAmount", + "aggregatedValue": "return record.paidAmount" + } + ], + "condition": "", + "nodeId": 21 + }, + { + "name": "written_off", + "type": "aggregateGroup", + "groupBy": "return record.invoiceType", + "aggregates": [ + { + "name": "totalIncVat", + "aggregatedValue": "return record.totalIncVat" + } + ], + "condition": "record.isWrittenOff === true", + "nodeId": 22 + } + ], + "allowedRecordNodeIds": [ + 8, + 6 + ], + "nodeId": 19 + }, + { + "name": "customersBySurname", + "type": "index", + "map": "return {...record};", + "filter": "", + "indexType": "ancestor", + "getShardName": "return !record.surname ? 'null' : record.surname.substring(0,1);", + "getSortKey": "record.id", + "aggregateGroups": [], + "allowedRecordNodeIds": [ + 2 + ], + "nodeId": 23 + } + ], + "nodeId": 0 +} \ No newline at end of file diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js new file mode 100644 index 000000000..6918c207d --- /dev/null +++ b/packages/builder/src/builderStore/index.js @@ -0,0 +1,19 @@ +import {createPackage} from "./createPackage"; +import getStore from "./store"; +import { last } from "lodash/fp"; + +const appName = last(window.location.hash.substr(1).split("/")); + +export const database = getStore(appName); + +export const createNewPackage = () => + createPackage(packageInfo, database); + +export const initialise = async () => { + try { + await database.initialise(); + } catch(err) { + console.log(err); + } + +} diff --git a/packages/builder/src/builderStore/store.js b/packages/builder/src/builderStore/store.js new file mode 100644 index 000000000..8e5be4623 --- /dev/null +++ b/packages/builder/src/builderStore/store.js @@ -0,0 +1,330 @@ +import {hierarchy as hierarchyFunctions, + common, getTemplateApi } from "budibase-core"; +import {filter, cloneDeep, sortBy, map, + find, isEmpty, groupBy, reduce} from "lodash/fp"; +import {chain, getNode, validate, + constructHierarchy, templateApi} from "../common/core"; +import {writable} from "svelte/store"; + +export const getStore = (appname) => { + + const initial = { + hierarchy: {}, + actions: [], + triggers: [], + currentNodeIsNew: false, + errors: [], + activeNav: "database", + hasAppPackage: (!!appname && appname.length > 0), + accessLevels: [], + currentNode: null}; + + const store = writable(initial); + + store.appname = appname; + store.initialise = initialise(store, initial, appname); + store.newChildRecord = newRecord(store, false); + store.newRootRecord = newRecord(store, true); + store.selectExistingNode = selectExistingNode(store); + store.newChildIndex = newIndex(store, false); + store.newRootIndex = newIndex(store, true); + store.saveCurrentNode = saveCurrentNode(store); + store.importAppDefinition = importAppDefinition(store); + store.deleteCurrentNode = deleteCurrentNode(store); + store.saveField = saveField(store); + store.deleteField = deleteField(store); + store.saveAction = saveAction(store); + store.deleteAction = deleteAction(store); + store.saveTrigger = saveTrigger(store); + store.deleteTrigger = deleteTrigger(store); + store.saveLevel = saveLevel(store); + store.deleteLevel = deleteLevel(store); + store.setActiveNav = setActiveNav(store); + return store; +} + +export default getStore; + +const initialise = (store, initial) => async () => { + + const pkg = await fetch(`/api/${store.appname}/appPackage`) + .then(r => r.json()); + + initial.hierarchy = pkg.appDefinition.hierarchy; + initial.accessLevels = pkg.accessLevels; + initial.actions = reduce((arr, action) => { + arr.push(action); + return arr; + })(pkg.appDefinition.actions, []); + initial.triggers = pkg.appDefinition.triggers; + + if(!!initial.hierarchy && !isEmpty(initial.hierarchy)) { + initial.hierarchy = constructHierarchy(initial.hierarchy); + const shadowHierarchy = createShadowHierarchy(initial.hierarchy); + if(initial.currentNode !== null) + initial.currentNode = getNode( + shadowHierarchy, initial.currentNode.nodeId + ); + } + store.set(initial); + return initial; +} + +const newRecord = (store, useRoot) => () => { + store.update(s => { + s.currentNodeIsNew = true; + const shadowHierarchy = createShadowHierarchy(s.hierarchy); + parent = useRoot ? shadowHierarchy + : getNode( + shadowHierarchy, + s.currentNode.nodeId); + s.errors = []; + s.currentNode = templateApi(shadowHierarchy) + .getNewRecordTemplate(parent, "", true); + return s; + }); +} + + +const selectExistingNode = (store) => (nodeId) => { + store.update(s => { + const shadowHierarchy = createShadowHierarchy(s.hierarchy); + s.currentNode = getNode( + shadowHierarchy, nodeId + ); + s.currentNodeIsNew = false; + s.errors = []; + return s; + }) +} + +const newIndex = (store, useRoot) => () => { + store.update(s => { + s.currentNodeIsNew = true; + s.errors = []; + const shadowHierarchy = createShadowHierarchy(s.hierarchy); + parent = useRoot ? shadowHierarchy + : getNode( + shadowHierarchy, + s.currentNode.nodeId); + + s.currentNode = templateApi(shadowHierarchy) + .getNewIndexTemplate(parent); + return s; + }); +} + +const saveCurrentNode = (store) => () => { + store.update(s => { + + const errors = validate.node(s.currentNode); + s.errors = errors; + if(errors.length > 0) { + return s; + } + + const parentNode = getNode( + s.hierarchy, s.currentNode.parent().nodeId); + + const existingNode = getNode( + s.hierarchy, s.currentNode.nodeId); + + let index = parentNode.children.length; + if(!!existingNode) { + // remove existing + index = existingNode.parent().children.indexOf(existingNode); + existingNode.parent().children = chain(existingNode.parent().children, [ + filter(c => c.nodeId !== existingNode.nodeId) + ]); + } + + // should add node into existing hierarchy + const cloned = cloneDeep(s.currentNode); + templateApi(s.hierarchy).constructNode( + parentNode, + cloned + ); + + const newIndexOfchild = child => { + if(child === cloned) return index; + const currentIndex = parentNode.children.indexOf(child); + return currentIndex >= index ? currentIndex + 1 : currentIndex; + } + + parentNode.children = chain(parentNode.children, [ + sortBy(newIndexOfchild) + ]); + + s.currentNodeIsNew = false; + + savePackage(store, s); + + return s; + }); +} + +const importAppDefinition = store => appDefinition => { + store.update(s => { + s.hierarchy = appDefinition.hierarchy; + s.currentNode = appDefinition.hierarchy.children.length > 0 + ? appDefinition.hierarchy.children[0] + : null; + s.actions = appDefinition.actions; + s.triggers = appDefinition.triggers; + s.currentNodeIsNew = false; + return s; + }); +} + +const deleteCurrentNode = store => () => { + store.update(s => { + const nodeToDelete = getNode(s.hierarchy, s.currentNode.nodeId); + s.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent()) + ? find(n => n != s.currentNode) + (s.hierarchy.children) + : nodeToDelete.parent(); + if(hierarchyFunctions.isRecord(nodeToDelete)) { + nodeToDelete.parent().children = filter(c => c.nodeId !== nodeToDelete.nodeId) + (nodeToDelete.parent().children); + } else { + nodeToDelete.parent().indexes = filter(c => c.nodeId !== nodeToDelete.nodeId) + (nodeToDelete.parent().indexes); + } + s.errors = []; + savePackage(store, s); + return s; + }); +} + +const saveField = databaseStore => (field) => { + databaseStore.update(db => { + db.currentNode.fields = filter(f => f.name !== field.name) + (db.currentNode.fields); + + templateApi(db.hierarchy).addField(db.currentNode, field); + return db; + }); +} + + +const deleteField = databaseStore => field => { + databaseStore.update(db => { + db.currentNode.fields = filter(f => f.name !== field.name) + (db.currentNode.fields); + + return db; + }); +} + + +const saveAction = store => (newAction, isNew, oldAction=null) => { + store.update(s => { + + const existingAction = isNew + ? null + : find(a => a.name === oldAction.name)(s.actions); + + if(existingAction) { + s.actions = chain(s.actions, [ + map(a => a === existingAction ? newAction : a) + ]); + } else { + s.actions.push(newAction); + } + savePackage(store, s); + return s; + }); +} + +const deleteAction = store => action => { + store.update(s => { + s.actions = filter(a => a.name !== action.name)(s.actions); + savePackage(store, s); + return s; + }); +} + +const saveTrigger = store => (newTrigger, isNew, oldTrigger=null) => { + store.update(s => { + + const existingTrigger = isNew + ? null + : find(a => a.name === oldTrigger.name)(s.triggers); + + if(existingTrigger) { + s.triggers = chain(s.triggers, [ + map(a => a === existingTrigger ? newTrigger : a) + ]); + } else { + s.triggers.push(newTrigger); + } + savePackage(store, s); + return s; + }); +} + +const deleteTrigger = store => trigger => { + store.update(s => { + s.triggers = filter(t => t.name !== trigger.name)(s.triggers); + return s; + }); +} + +const saveLevel = store => (newLevel, isNew, oldLevel=null) => { + store.update(s => { + + const existingLevel = isNew + ? null + : find(a => a.name === oldLevel.name)(s.accessLevels); + + if(existingLevel) { + s.accessLevels = chain(s.accessLevels, [ + map(a => a === existingLevel ? newLevel : a) + ]); + } else { + s.accessLevels.push(newLevel); + } + savePackage(store, s); + return s; + }); +} + +const deleteLevel = store => level => { + store.update(s => { + s.accessLevels = filter(t => t.name !== level.name)(s.accessLevels); + savePackage(store, s); + return s; + }); +} + +const setActiveNav = databaseStore => navName => { + databaseStore.update(db => { + db.activeNav = navName; + return db; + }); +} + +const createShadowHierarchy = hierarchy => + constructHierarchy(JSON.parse(JSON.stringify(hierarchy))); + +const savePackage = (store, db) => { + + const appDefinition = { + hierarchy:db.hierarchy, + triggers:db.triggers, + actions: groupBy("name")(db.actions) + }; + + const data = { + appDefinition, + accessLevels:db.accessLevels + } + + fetch(`/api/${store.appname}/appPackage`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), // body data type must match "Content-Type" header + }); +} diff --git a/packages/builder/src/common/Button.svelte b/packages/builder/src/common/Button.svelte new file mode 100644 index 000000000..7bd6f8e96 --- /dev/null +++ b/packages/builder/src/common/Button.svelte @@ -0,0 +1,161 @@ + + + + + \ No newline at end of file diff --git a/packages/builder/src/common/ButtonGroup.svelte b/packages/builder/src/common/ButtonGroup.svelte new file mode 100644 index 000000000..e0842f08d --- /dev/null +++ b/packages/builder/src/common/ButtonGroup.svelte @@ -0,0 +1,27 @@ + + +
+ +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/Checkbox.svelte b/packages/builder/src/common/Checkbox.svelte new file mode 100644 index 000000000..c1cea90df --- /dev/null +++ b/packages/builder/src/common/Checkbox.svelte @@ -0,0 +1,16 @@ + + +{label} + + \ No newline at end of file diff --git a/packages/builder/src/common/CodeArea.svelte b/packages/builder/src/common/CodeArea.svelte new file mode 100644 index 000000000..29208130c --- /dev/null +++ b/packages/builder/src/common/CodeArea.svelte @@ -0,0 +1,21 @@ + + +
{label}
+ + + \ No newline at end of file diff --git a/packages/builder/src/common/ComingSoon.svelte b/packages/builder/src/common/ComingSoon.svelte new file mode 100644 index 000000000..328ccc24a --- /dev/null +++ b/packages/builder/src/common/ComingSoon.svelte @@ -0,0 +1,17 @@ + + +
+

Coming Sometime: {name}

+
+ + \ No newline at end of file diff --git a/packages/builder/src/common/DatePicker.svelte b/packages/builder/src/common/DatePicker.svelte new file mode 100644 index 000000000..8f09f9091 --- /dev/null +++ b/packages/builder/src/common/DatePicker.svelte @@ -0,0 +1,61 @@ + + + +
+
{label}
+ +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/Dropdown.svelte b/packages/builder/src/common/Dropdown.svelte new file mode 100644 index 000000000..cbe9b9822 --- /dev/null +++ b/packages/builder/src/common/Dropdown.svelte @@ -0,0 +1,59 @@ + + +
+
{label}
+ + {#if multiple} + + + + {:else} + + + {/if} + +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/DropdownButton.svelte b/packages/builder/src/common/DropdownButton.svelte new file mode 100644 index 000000000..61808a74b --- /dev/null +++ b/packages/builder/src/common/DropdownButton.svelte @@ -0,0 +1,68 @@ + + + +
isDroppedDown = !isDroppedDown}> + {@html getIcon(iconName)} + + + + + +
+ + + \ No newline at end of file diff --git a/packages/builder/src/common/ErrorsBox.svelte b/packages/builder/src/common/ErrorsBox.svelte new file mode 100644 index 000000000..3d863f19e --- /dev/null +++ b/packages/builder/src/common/ErrorsBox.svelte @@ -0,0 +1,29 @@ + + +{#if hasErrors} +
+ {#each errors as error} +
{error.field ? `${error.field}: ` : ""}{error.error}
+ {/each} +
+{/if} + + \ No newline at end of file diff --git a/packages/builder/src/common/Modal.svelte b/packages/builder/src/common/Modal.svelte new file mode 100644 index 000000000..6714ba12a --- /dev/null +++ b/packages/builder/src/common/Modal.svelte @@ -0,0 +1,36 @@ + + +
+
+ +
+
+ + + \ No newline at end of file diff --git a/packages/builder/src/common/NumberBox.svelte b/packages/builder/src/common/NumberBox.svelte new file mode 100644 index 000000000..69c471e34 --- /dev/null +++ b/packages/builder/src/common/NumberBox.svelte @@ -0,0 +1,46 @@ + + +
+
{label}
+ +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/Textbox.svelte b/packages/builder/src/common/Textbox.svelte new file mode 100644 index 000000000..f0386da82 --- /dev/null +++ b/packages/builder/src/common/Textbox.svelte @@ -0,0 +1,32 @@ + + +
+
{label}
+ +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/ValuesList.svelte b/packages/builder/src/common/ValuesList.svelte new file mode 100644 index 000000000..ef9039a7f --- /dev/null +++ b/packages/builder/src/common/ValuesList.svelte @@ -0,0 +1,47 @@ + + +
+
{label}
+ +
+ + \ No newline at end of file diff --git a/packages/builder/src/common/core.js b/packages/builder/src/common/core.js new file mode 100644 index 000000000..4b9302794 --- /dev/null +++ b/packages/builder/src/common/core.js @@ -0,0 +1,75 @@ +import {hierarchy as hierarchyFunctions, + common, getTemplateApi, getAuthApi } from "budibase-core"; +import {find, filter, includes, keyBy, some, + flatten, map} from "lodash/fp"; + +export const chain = common.$; + +export const events = common.eventsList; + +export const getNode = (hierarchy, nodeId) => + chain(hierarchy, [ + hierarchyFunctions.getFlattenedHierarchy, + find(n => n.nodeId === nodeId || n.nodeKey() === nodeId) + ]); + +export const constructHierarchy = node => { + if(!node) return node; + return templateApi(node).constructHeirarchy(node); +} + +export const createNewHeirarchy = () => { + return templateApi().getNewRootLevel(); +} + +export const templateApi = hierarchy => getTemplateApi({heirarchy:hierarchy}) +export const authApi = (hierarchy, actions) => getAuthApi({ + heirarchy:hierarchy, actions: keyBy("name")(actions), publish:()=>{}}) + +export const allTypes = templateApi({}).allTypes; + +export const validate = { + all: templateApi({}).validateAll, + node: templateApi({}).validateNode, + field: templateApi({}).validateField +}; + +export const getPotentialReverseReferenceIndexes = (hierarchy, refIndex) => { + const res = chain(hierarchy, [ + hierarchyFunctions.getFlattenedHierarchy, + filter(n => hierarchyFunctions.isAncestor(refIndex)(n) + || hierarchyFunctions.isAncestor(refIndex)(n.parent())), + map(n => n.indexes), + flatten, + filter(hierarchyFunctions.isReferenceIndex) + ]); + + return res; +} + +export const getPotentialReferenceIndexes = (hierarchy, record) => + chain(hierarchy, [ + hierarchyFunctions.getFlattenedHierarchy, + filter(hierarchyFunctions.isAncestorIndex), + filter(i => hierarchyFunctions.isAncestor(record)(i.parent()) + || i.parent().nodeId === record.parent().nodeId + || hierarchyFunctions.isRoot(i.parent())) + ]); + +export const getDefaultTypeOptions = type => + !type ? {} : allTypes[type].getDefaultOptions(); + +export const getNewAction = () => templateApi({}).createAction(); +export const getNewTrigger = () => templateApi({}).createTrigger(); + +export const validateActions = actions => templateApi({}).validateActions(actions); +export const validateTriggers = (triggers, actions) => templateApi({}).validateTriggers(triggers, actions); + +export const generateFullPermissions = (hierarchy, actions) => + authApi(hierarchy,actions).generateFullPermissions(); + +export const getNewAccessLevel = () => + authApi().getNewAccessLevel(); + +export const validateAccessLevels = (hierarchy, actions, accessLevels) => + authApi(hierarchy, actions).validateAccessLevels(accessLevels); \ No newline at end of file diff --git a/packages/builder/src/common/icon.js b/packages/builder/src/common/icon.js new file mode 100644 index 000000000..4648d4ebf --- /dev/null +++ b/packages/builder/src/common/icon.js @@ -0,0 +1,2 @@ +import feather from "feather-icons"; +export default icon => feather.toSvg(icon); \ No newline at end of file diff --git a/packages/builder/src/database/ActionsHeader.svelte b/packages/builder/src/database/ActionsHeader.svelte new file mode 100644 index 000000000..bf1196254 --- /dev/null +++ b/packages/builder/src/database/ActionsHeader.svelte @@ -0,0 +1,62 @@ + + +
+ + + + + {#if !$database.currentNodeIsNew} + + {/if} + + + {#if !!$database.errors && $database.errors.length > 0} +
+ +
+ {/if} + + +
Are you sure you want to delete {$database.currentNode.name} ?
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/packages/builder/src/database/CollectionView.svelte b/packages/builder/src/database/CollectionView.svelte new file mode 100644 index 000000000..b4006b73f --- /dev/null +++ b/packages/builder/src/database/CollectionView.svelte @@ -0,0 +1,20 @@ + + +
+
{record.name}
+
+
{record.nodeKey()}
+
+
+ + \ No newline at end of file diff --git a/packages/builder/src/database/DatabaseRoot.svelte b/packages/builder/src/database/DatabaseRoot.svelte new file mode 100644 index 000000000..cd7fcbd80 --- /dev/null +++ b/packages/builder/src/database/DatabaseRoot.svelte @@ -0,0 +1,129 @@ + + +
+
+
+
Records
+ +
+ {#each $database.hierarchy.children as record} + + {/each} + +
+
Indexes
+ +
+ {#each $database.hierarchy.indexes as index} + + {/each} +
+
+
+ {#if $database.currentNode} + + {/if} +
+
+ {#if !$database.currentNode} +

:)

+ {:else if $database.currentNode.type === "record"} + + {:else} + + {/if} +
+
+
+ + + \ No newline at end of file diff --git a/packages/builder/src/database/FieldView.svelte b/packages/builder/src/database/FieldView.svelte new file mode 100644 index 000000000..5e26dd0c3 --- /dev/null +++ b/packages/builder/src/database/FieldView.svelte @@ -0,0 +1,117 @@ + + +
+ + + + + + {#if isNew} + + {:else} +
{clonedField.name}
+ {/if} + + + + {#if clonedField.type === "string"} + + + + {:else if clonedField.type === "bool"} + + {:else if clonedField.type === "datetime"} + + + {:else if clonedField.type === "number"} + + + + {:else if clonedField.type === "reference"} + n.nodeKey()} + textMember={n => n.name} + bind:selected={clonedField.typeOptions.indexNodeKey} /> + + n.nodeKey()} + textMember={n => n.name} + bind:selected={clonedField.typeOptions.reverseIndexNodeKeys} /> + + + + {:else if clonedField.type.startsWith("array")} + + + {/if} + + + + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/database/HierarchyRow.svelte b/packages/builder/src/database/HierarchyRow.svelte new file mode 100644 index 000000000..7d5ec35d0 --- /dev/null +++ b/packages/builder/src/database/HierarchyRow.svelte @@ -0,0 +1,44 @@ + + +
+
database.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> + {node.name} +
+ {#if node.children} + {#each node.children as child} + + {/each} + {/if} +
+ + + \ No newline at end of file diff --git a/packages/builder/src/database/IndexView.svelte b/packages/builder/src/database/IndexView.svelte new file mode 100644 index 000000000..c1f5bfd7a --- /dev/null +++ b/packages/builder/src/database/IndexView.svelte @@ -0,0 +1,76 @@ + + +
+ + +
+
Records to Index
+ {#each indexableRecords as rec} + toggleAllowedRecord(rec)}/> + {rec.node.name} + {/each} +
+ + + + + + + + + +
+ + \ No newline at end of file diff --git a/packages/builder/src/database/RecordView.svelte b/packages/builder/src/database/RecordView.svelte new file mode 100644 index 000000000..ea3be006c --- /dev/null +++ b/packages/builder/src/database/RecordView.svelte @@ -0,0 +1,258 @@ + + +
+ + +
{record.nodeKey()}
+ {#if !record.isSingle} + + + {/if} + +

+ Fields {@html getIcon("plus")} +

+ + {#if record.fields.length > 0} + + + + + + + + + + + {#each record.fields as field} + + + + + + + {/each} + +
NameTypeOptions
+
{field.label}
+
{field.name}
+
{field.type}{@html getTypeOptions(field.typeOptions)} + editField(field)}>{@html getIcon("edit")} + deleteField(field)}>{@html getIcon("trash")} +
+ {:else} + (no fields added) + {/if} + + {#if editingField} + + + + {/if} + +

+ Indexes +

+ + {#each record.indexes as index} +
+
+ {index.name} + editIndex(index)}>{@html getIcon("edit")} +
+
+ records indexed: + {getIndexAllowedRecords(index)} + type: + {index.indexType} +
+
+ map: + {index.map} +
+ {#if index.filter} +
+ filter: + {index.filter} +
+ {/if} +
+ {:else} + (no indexes added) + {/each} + +
+ + + \ No newline at end of file diff --git a/packages/builder/src/favicon.png b/packages/builder/src/favicon.png new file mode 100644 index 000000000..897909662 Binary files /dev/null and b/packages/builder/src/favicon.png differ diff --git a/packages/builder/src/fonts.css b/packages/builder/src/fonts.css new file mode 100644 index 000000000..4901cee9f --- /dev/null +++ b/packages/builder/src/fonts.css @@ -0,0 +1,61 @@ +/* latin-ext */ +@font-face { + font-family: 'Lato'; + font-weight: 400; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin-ext.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + /* latin */ +@font-face { + font-family: 'Lato'; + font-weight: 400; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + + +/* Black latin-ext */ +@font-face { + font-family: 'Lato Black'; + font-weight: 900; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin-ext.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + /* Black latin */ +@font-face { + font-family: 'Lato Black'; + font-weight: 900; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* Bold latin-ext */ +@font-face { + font-family: 'Lato Bold'; + font-weight: 700; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin-ext.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + /* Bold latin */ +@font-face { + font-family: 'Lato Bold'; + font-weight: 700; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* Semi Bold latin-ext */ +@font-face { + font-family: 'Lato Semi Bold'; + font-weight: 600; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin-ext.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + /* Semi Bold latin */ +@font-face { + font-family: 'Lato Semi Bold'; + font-weight: 600; + src: local('Lato Regular'), local('Lato-Regular'), url(./assets/lato-latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + \ No newline at end of file diff --git a/packages/builder/src/global.css b/packages/builder/src/global.css new file mode 100644 index 000000000..241ae0a87 --- /dev/null +++ b/packages/builder/src/global.css @@ -0,0 +1,91 @@ +:root { + --primary100: #454CA0FF; + --primary75: #454CA0BF; + --primary25: #454CA040; + --primary10: #454CA01A; + --primarydark: #3F448A; + + --secondary100: #162B4DFF; + --secondary75: #162B4DBF; + --secondary25: #162B4D40; + --secondary10: #162B4D1A; + --secondarydark: #3F448A; + + --tertiary: #F2F5F7; + + --success100: #49C39EFF; + --success75: #49C39EBF; + --success25: #49C39E40; + --success10: #49C39E1A; + --successdark: #44B492; + + --deletion100: #F2545BFF; + --deletion75: #F2545BBF; + --deletion25: #F2545B40; + --deletion10: #F2545B1A; + --deletiondark: #CF4046; + + --white: #FFFFFF; + --darkslate: #5C6B82; + --slate: #8B95A6; + + --borderradius: 2px; + --borderradiusall: 2px 2px 2px 2px; + + --fontblack: "Lato Black"; + --fontbold: "Lato Bold"; + --fontsemibold: "Lato Semi Bold"; + --fontnormal: "Lato"; + + --bodytext: var(--fontnormal) "regular" var(--secondary100) 16pt; + --bigbodytext: var(--fontnormal) "regular" var(--secondary100) 20pt; + --smallbodytext: var(--fontnormal) "regular" var(--secondary100) 12pt; + --lightbodytext: var(--fontnormal) "regular" var(--darkslate) 16pt; + --heavybodytext: var(--fontbold) "regular" var(--secondary100) 16pt; + --quotation: var(--fontnormal) "italics" var(--darkslate) 16pt; + --smallheavybodytext: var(--fontbold) "regular" var(--secondary100) 14pt; + +} + +html, body { + font-family: var(--fontnormal); + color: var(--secondary100); + padding: 0; + margin: 0; +} + +h1 { + font-family: var(--fontblack); + font-size: 36pt; + color: var(--secondary100); +} + +h2 { + font-family: var(--fontbold); + font-size: 30pt; + color: var(--secondary100); +} + +h3 { + font-family: var(--fontbold); + font-size: 24pt; + color: var(--darkslate); +} + +h4 { + font-family: var(--fontsemibold); + font-size: 18pt; + color: var(--secondary100); +} + +h5 { + font-family: var(--fontblack); + font-size: 14pt; + color: var(--secondary100); +} + +h5 { + font-family: var(--fontblack); + font-size: 12pt; + color: var(--darkslate); +} \ No newline at end of file diff --git a/packages/builder/src/imgs/budibase-logo-only.png b/packages/builder/src/imgs/budibase-logo-only.png new file mode 100644 index 000000000..652444e5b Binary files /dev/null and b/packages/builder/src/imgs/budibase-logo-only.png differ diff --git a/packages/builder/src/imgs/budibase-logo-white.png b/packages/builder/src/imgs/budibase-logo-white.png new file mode 100644 index 000000000..da8440c09 Binary files /dev/null and b/packages/builder/src/imgs/budibase-logo-white.png differ diff --git a/packages/builder/src/imgs/budibase-logo.png b/packages/builder/src/imgs/budibase-logo.png new file mode 100644 index 000000000..1d6fc3d1c Binary files /dev/null and b/packages/builder/src/imgs/budibase-logo.png differ diff --git a/packages/builder/src/index.html b/packages/builder/src/index.html new file mode 100644 index 000000000..8f5b05b48 --- /dev/null +++ b/packages/builder/src/index.html @@ -0,0 +1,18 @@ + + + + + + + Budibase Builder + + + + + + + + + + + \ No newline at end of file diff --git a/packages/builder/src/main.js b/packages/builder/src/main.js new file mode 100644 index 000000000..ef35ef2a8 --- /dev/null +++ b/packages/builder/src/main.js @@ -0,0 +1,15 @@ +import App from "./App.svelte"; +import "./global.css"; +import "./fonts.css"; +import "./assets/lato-latin-ext.woff2"; +import "./assets/lato-latin.woff2"; +import "./assets/budibase-logo.png"; +import "./assets/budibase-logo-only.png"; +import "uikit/dist/css/uikit.min.css"; +import "uikit/dist/js/uikit.min.js"; + +const app = new App({ + target: document.getElementById("app") +}); + + diff --git a/packages/builder/src/nav/Nav.svelte b/packages/builder/src/nav/Nav.svelte new file mode 100644 index 000000000..01a9d3ce5 --- /dev/null +++ b/packages/builder/src/nav/Nav.svelte @@ -0,0 +1,38 @@ + + + + + + + \ No newline at end of file diff --git a/packages/builder/src/nav/NavItem.svelte b/packages/builder/src/nav/NavItem.svelte new file mode 100644 index 000000000..5feebab58 --- /dev/null +++ b/packages/builder/src/nav/NavItem.svelte @@ -0,0 +1,75 @@ + + +
+ +
+ + + \ No newline at end of file diff --git a/packages/builder/src/userInterface/UserInterfaceRoot.svelte b/packages/builder/src/userInterface/UserInterfaceRoot.svelte new file mode 100644 index 000000000..5ad9d7a3f --- /dev/null +++ b/packages/builder/src/userInterface/UserInterfaceRoot.svelte @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/packages/core b/packages/core index ab14ab9db..d418f210e 160000 --- a/packages/core +++ b/packages/core @@ -1 +1 @@ -Subproject commit ab14ab9db1698a82b1f98b692ecfa6b71c17752a +Subproject commit d418f210e2af6f6a37d590ec255730a0da2241c7 diff --git a/packages/server/.vscode/launch.json b/packages/server/.vscode/launch.json index 1c6688a46..023c59ffd 100644 --- a/packages/server/.vscode/launch.json +++ b/packages/server/.vscode/launch.json @@ -4,35 +4,32 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Jest All", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": ["--runInBand"], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true, - "windows": { - "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest", - } - }, - { - "type": "node", - "request": "launch", - "name": "Jest Current File", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": [ - "${fileBasenameNoExtension}", - "--config", - "jest.config.js" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true, - "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest", - } + { + "type": "node", + "request": "launch", + "name": "Start Server", + "program": "${workspaceFolder}/index.js" + }, + { + "type": "node", + "request": "launch", + "name": "Jest All", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest", } + }, + { + "type": "node", + "request": "launch", + "name": "Initialise Budibase", + "program": "${workspaceFolder}/initialise/initialiseBudibase.js", + "args": ["--datastore=local", "--username=test", "--password=test", "--rootPath=./.data", "--cleanDev"], + "console": "externalTerminal" + } ] } \ No newline at end of file diff --git a/packages/server/app.js b/packages/server/app.js index d4c492642..6b288b9e0 100644 --- a/packages/server/app.js +++ b/packages/server/app.js @@ -12,9 +12,9 @@ module.exports = async (budibaseContext) => { app.context.getAppPackage = await initialiseRuntimePackages( budibaseContext, app.context.master, - config.latestAppsPath + config.latestPackagesFolder ); app.use(koaBody({ multipart : true })); app.use(router(config, app).routes()); - return app.listen(); + return app.listen(config.port); }; diff --git a/packages/server/appPackages/testApp/dist/package.tar.gz b/packages/server/appPackages/testApp/dist/package.tar.gz index 49eb5212f..26e2b97a8 100644 Binary files a/packages/server/appPackages/testApp/dist/package.tar.gz and b/packages/server/appPackages/testApp/dist/package.tar.gz differ diff --git a/packages/server/config.js b/packages/server/config.js index b42293059..57e35921b 100644 --- a/packages/server/config.js +++ b/packages/server/config.js @@ -7,7 +7,8 @@ module.exports = () => ({ }, keys: ["secret1", "secret2"], port: 4001, - latestAppsPath: "./appPackages", + latestPackagesFolder: "appPackages", extraMasterPlugins: {}, + dev:true, customizeMaster: appDefinition => appDefinition }) \ No newline at end of file diff --git a/packages/server/config.sample.js b/packages/server/config.sample.js index 355c29d3a..165757c2a 100644 --- a/packages/server/config.sample.js +++ b/packages/server/config.sample.js @@ -18,12 +18,15 @@ module.exports = () => ({ port: 4001, // path to where your appDefinition etc is stored (dev time) - latestAppsPath: "./", + latestPackagesFolder: "./", // register plugins for master extraMasterPlugins: {}, // make modifications to master's appdefinition - e.g. add plugins - customizeMaster: appDefinition => appDefinition + customizeMaster: appDefinition => appDefinition, + + // false for production - serves builder if true + dev:false }) \ No newline at end of file diff --git a/packages/server/index.js b/packages/server/index.js index ae369e079..e47b44aa1 100644 --- a/packages/server/index.js +++ b/packages/server/index.js @@ -2,5 +2,10 @@ const app = require("./app"); const config = require("./config"); const buildAppContext = require("./initialise/buildAppContext"); -buildAppContext(config, true) - .then((appContext) => app(appContext)); \ No newline at end of file +(async () => { + const bbContext = await buildAppContext(config(), true); + const server = await app(bbContext); + server.on("listening", () => { + console.log(`Budibase Server listening on port ${bbContext.config.port}`); + }) +})() diff --git a/packages/server/initialise/initialiseBudibase.js b/packages/server/initialise/initialiseBudibase.js index 3d9f9d4b1..9775c8740 100644 --- a/packages/server/initialise/initialiseBudibase.js +++ b/packages/server/initialise/initialiseBudibase.js @@ -67,7 +67,7 @@ const question = async (q) => { await mkdir(rootconfig.rootPath); } - const appContext = await buildAppContext(budibaseConfig, false); + const appContext = await buildAppContext(budibaseConfig(), false); await create( appContext, datastoreModule, diff --git a/packages/server/middleware/routers.js b/packages/server/middleware/routers.js index e669c1e22..e9fbec67d 100644 --- a/packages/server/middleware/routers.js +++ b/packages/server/middleware/routers.js @@ -2,6 +2,7 @@ const Router = require("@koa/router"); const session = require("./session"); const StatusCodes = require("../utilities/statusCodes"); const fs = require("fs"); +const { getPackageForBuilder, savePackage } = require("../utilities/builder"); module.exports = (config, app) => { @@ -19,6 +20,10 @@ module.exports = (config, app) => { ctx.session.accessed = true; await next(); }) + .get("/_builder", async (ctx) => { + ctx.response.status = StatusCodes.OK; + ctx.response.body = "Builder UI Served Here"; + }) .get("/:appname", async (ctx) => { ctx.response.status = StatusCodes.OK; ctx.response.body = "UI Served Here"; @@ -69,6 +74,32 @@ module.exports = (config, app) => { ctx.response.status = StatusCodes.OK; }) + .get("/_builder/api/:appname/appPackage", async (ctx) => { + if(!config.dev) { + ctx.request.status = StatusCodes.FORBIDDEN; + ctx.request.body = "run in dev mode to access builder"; + return; + } + + ctx.body = await getPackageForBuilder( + config, + ctx.params.appname); + ctx.response.status = StatusCodes.OK; + + }) + .post("/_builder/api/:appname/appPackage", async (ctx) => { + if(!config.dev) { + ctx.request.status = StatusCodes.FORBIDDEN; + ctx.body = "run in dev mode to access builder"; + return; + } + + ctx.body = await savePackage( + config, + ctx.params.appname, + ctx.request.body); + ctx.reqponse.status = StatusCodes.OK; + }) .use(async (ctx, next) => { const pathParts = ctx.path.split("/"); diff --git a/packages/server/runtime_apps/testApp/3-VYAktSELSd/access_levels.json b/packages/server/runtime_apps/testApp/3-VYAktSELSd/access_levels.json deleted file mode 100644 index 4e01e4e9a..000000000 --- a/packages/server/runtime_apps/testApp/3-VYAktSELSd/access_levels.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "levels": [ - { - "name": "owner", - "permissions": [ - { - "type": "create record", - "nodeKey": "/customers/1-{id}" - }, - { - "type": "delete record", - "nodeKey": "/customers/1-{id}" - }, - { - "type": "update record", - "nodeKey": "/customers/1-{id}" - }, - { - "type": "read record", - "nodeKey": "/customers/1-{id}" - }, - { - "type": "create record", - "nodeKey": "/customers/1-{id}/invoices/2-{id}" - }, - { - "type": "update record", - "nodeKey": "/customers/1-{id}/invoices/2-{id}" - }, - { - "type": "delete record", - "nodeKey": "/customers/1-{id}/invoices/2-{id}" - }, - { - "type": "read record", - "nodeKey": "/customers/1-{id}/invoices/2-{id}" - }, - { - "type": "write templates" - }, - { - "type": "create user" - }, - { - "type": "set password" - }, - { - "type": "create temporary access" - }, - { - "type": "enable or disable user" - }, - { - "type": "write access levels" - }, - { - "type": "list users" - }, - { - "type": "list access levels" - }, - { - "type": "manage index" - }, - { - "type": "set user access levels" - }, - { - "type": "manage collection" - } - ] - } - ], - "version": 0 -} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-VYAktSELSd/appDefinition.json b/packages/server/runtime_apps/testApp/3-VYAktSELSd/appDefinition.json deleted file mode 100644 index 871524cb9..000000000 --- a/packages/server/runtime_apps/testApp/3-VYAktSELSd/appDefinition.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "hierarchy": { - "name": "root", - "type": "root", - "children": [ - { - "name": "customer", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 1000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "name", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [ - { - "name": "invoice", - "type": "record", - "fields": [ - { - "name": "amount", - "type": "number", - "typeOptions": { - "minValue": 99999999999, - "maxValue": 99999999999, - "decimalPlaces": 2 - }, - "label": "amount", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 2, - "indexes": [], - "allidsShardFactor": 1, - "collectionName": "invoices", - "isSingle": false - } - ], - "validationRules": [], - "nodeId": 1, - "indexes": [], - "allidsShardFactor": 64, - "collectionName": "customers", - "isSingle": false - } - ], - "pathMaps": [], - "indexes": [], - "nodeId": 0 - }, - "actions": { - "output_to_file": { - "name": "output_to_file", - "behaviourSource": "main", - "behaviourName": "outputToFile", - "initialOptions": {} - } - }, - "triggers": [ - { - "actionName": "output_to_file", - "eventName": "authApi:createUser:onComplete", - "optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };", - "condition": "!context.password" - }, - { - "actionName": "output_to_file", - "eventName": "authApi:createTemporaryAccess:onComplete", - "optionsCreator": "return { filename:'tempaccess' + context.userName, content:context.result };", - "condition": "" - } - ] -} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-VYAktSELSd/plugins.js b/packages/server/runtime_apps/testApp/3-VYAktSELSd/plugins.js deleted file mode 100644 index 838a10a7f..000000000 --- a/packages/server/runtime_apps/testApp/3-VYAktSELSd/plugins.js +++ /dev/null @@ -1,9 +0,0 @@ -const fs = require("fs"); - -module.exports = (config) => ({ - main: { - outputToFile : ({filename, content}) => { - fs.writeFile(`./tests/.data/${filename}`, content, {encoding:"utf8"}); - } - } -}) \ No newline at end of file diff --git a/packages/server/tests/testApp.js b/packages/server/tests/testApp.js index 004c41e25..2009a9aa6 100644 --- a/packages/server/tests/testApp.js +++ b/packages/server/tests/testApp.js @@ -44,7 +44,7 @@ const config = { }, keys: ["secret1", "secret2"], port: 4002, - latestAppsPath: "./appPackages", + latestPackagesFolder: "./appPackages", extraMasterPlugins, customizeMaster } @@ -61,7 +61,7 @@ module.exports = () => { const budibaseContext = await buildAppContext(config, true); server = await app(budibaseContext); } catch(e) { - console.log(e.message); + console.log(e.stack); } enableDestroy(server); }, diff --git a/packages/server/utilities/builder.js b/packages/server/utilities/builder.js new file mode 100644 index 000000000..7aa5e4893 --- /dev/null +++ b/packages/server/utilities/builder.js @@ -0,0 +1,31 @@ +const { appPackageFolder } = require("./createAppPackage"); +const { writeFile, readFile } = require("./fsawait"); + +module.exports.getPackageForBuilder = async (config, appname) => { + const appPath = appPackageFolder(config, appname); + return ({ + appDefinition: JSON.parse(await readFile( + `${appPath}/appDefinition.json`, + "utf8")), + + accessLevels: JSON.parse(await readFile( + `${appPath}/access_levels.json`, + "utf8")) + }) + +} + + +module.exports.savePackage = async (config, appname, pkg) => { + const appPath = appPackageFolder(config, appname); + await writeFile( + `${appPath}/appDefinition.json`, + JSON.stringify(pkg.appDefinition), + "utf8"); + + await writeFile( + `${appPath}/access_levels.json`, + JSON.stringify(pkg.accessLevels), + "utf8"); + +} \ No newline at end of file diff --git a/packages/server/utilities/createAppPackage.js b/packages/server/utilities/createAppPackage.js index a879c961b..d38253d51 100644 --- a/packages/server/utilities/createAppPackage.js +++ b/packages/server/utilities/createAppPackage.js @@ -1,8 +1,9 @@ -const { join } = require("path"); +const { resolve, join } = require("path"); const constructHierarchy = require("./constructHierarchy"); const { common } = require("budibase-core"); const { getRuntimePackageDirectory } = require("../utilities/runtimePackages"); const injectPlugins = require("./injectedPlugins"); +const { cwd } = require("process"); const createAppPackage = (context, appPath) => { @@ -24,9 +25,15 @@ const createAppPackage = (context, appPath) => { }) } +const appPackageFolder = (config, appname) => + resolve(cwd(), config.latestPackagesFolder, appname); + +module.exports.appPackageFolder = appPackageFolder; + module.exports.masterAppPackage = (context) => { const { config } = context; - const standardPackage = createAppPackage(context, "../appPackages/master"); + const standardPackage = createAppPackage( + context, "../appPackages/master"); const customizeMaster = config && config.customizeMaster ? config.customizeMaster @@ -37,8 +44,7 @@ module.exports.masterAppPackage = (context) => { constructHierarchy ]); - const plugins = require("../appPackages/master/plugins.js") - (context); + const plugins = standardPackage.behaviourSources; return ({ appDefinition, diff --git a/packages/server/utilities/fsawait.js b/packages/server/utilities/fsawait.js index b2c99adc2..49965e193 100644 --- a/packages/server/utilities/fsawait.js +++ b/packages/server/utilities/fsawait.js @@ -2,6 +2,7 @@ const util = require("util"); const fs = require("fs"); module.exports.readFile = util.promisify(fs.readFile); +module.exports.writeFile = util.promisify(fs.writeFile); module.exports.rimraf = util.promisify(require("rimraf")); module.exports.mkdir = util.promisify(fs.mkdir); module.exports.unlink = util.promisify(fs.unlink); diff --git a/packages/server/utilities/targzAppPackage.js b/packages/server/utilities/targzAppPackage.js index b35f3f08a..f88ba5471 100644 --- a/packages/server/utilities/targzAppPackage.js +++ b/packages/server/utilities/targzAppPackage.js @@ -10,7 +10,7 @@ const { getRuntimePackageDirectory, getRuntimeAppsDirectory } = require("./runti module.exports.createTarGzPackage = async (config, appName) => { - const appPath = join(config.latestAppsPath, appName); + const appPath = join(config.latestPackagesFolder, appName); const distPath = join(appPath, "dist"); if(!await exists(distPath)) { diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index a755e7449..04a696124 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -2498,9 +2498,9 @@ lodash.sortby@^4.7.0: integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= lodash@^4.17.11: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + version "4.17.14" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" + integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== loose-envify@^1.0.0: version "1.4.0" @@ -3362,11 +3362,16 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"