diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..19abd99 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,151 @@ +### Contributing ### + +Thank you for your interest in `loopback-phase`, an open source project +administered by StrongLoop. + +Contributing to `loopback-phase` is easy. In a few simple steps: + + * Ensure that your effort is aligned with the project's roadmap by + talking to the maintainers, especially if you are going to spend a + lot of time on it. + + * Make something better or fix a bug. + + * Adhere to code style outlined in the [Google C++ Style Guide][] and + [Google Javascript Style Guide][]. + + * Sign the [Contributor License Agreement](https://cla.strongloop.com/strongloop/loopback-phase) + + * Submit a pull request through Github. + + +### Contributor License Agreement ### + +``` + Individual Contributor License Agreement + + By signing this Individual Contributor License Agreement + ("Agreement"), and making a Contribution (as defined below) to + StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and + agree to the following terms and conditions for Your present and + future Contributions submitted to StrongLoop. Except for the license + granted in this Agreement to StrongLoop and recipients of software + distributed by StrongLoop, You reserve all right, title, and interest + in and to Your Contributions. + + 1. Definitions + + "You" or "Your" shall mean the copyright owner or the individual + authorized by the copyright owner that is entering into this + Agreement with StrongLoop. + + "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, that + is intentionally submitted by You to StrongLoop for inclusion in, + or documentation of, any of the products owned or managed by + StrongLoop ("Work"). For purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication + sent to StrongLoop or its representatives, including but not + limited to communication or electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, + or on behalf of, StrongLoop for the purpose of discussing and + improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by You as + "Not a Contribution." + + 2. You Grant a Copyright License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and recipients of software distributed by + StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable copyright license to reproduce, prepare + derivative works of, publicly display, publicly perform, + sublicense, and distribute Your Contributions and such derivative + works under any license and without any restrictions. + + 3. You Grant a Patent License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and to recipients of software distributed by + StrongLoop a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this Section) + patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work under any license and + without any restrictions. The patent license You grant to + StrongLoop under this Section applies only to those patent claims + licensable by You that are necessarily infringed by Your + Contributions(s) alone or by combination of Your Contributions(s) + with the Work to which such Contribution(s) was submitted. If any + entity institutes a patent litigation against You or any other + entity (including a cross-claim or counterclaim in a lawsuit) + alleging that Your Contribution, or the Work to which You have + contributed, constitutes direct or contributory patent + infringement, any patent licenses granted to that entity under + this Agreement for that Contribution or Work shall terminate as + of the date such litigation is filed. + + 4. You Have the Right to Grant Licenses to StrongLoop + + You represent that You are legally entitled to grant the licenses + in this Agreement. + + If Your employer(s) has rights to intellectual property that You + create, You represent that You have received permission to make + the Contributions on behalf of that employer, that Your employer + has waived such rights for Your Contributions, or that Your + employer has executed a separate Corporate Contributor License + Agreement with StrongLoop. + + 5. The Contributions Are Your Original Work + + You represent that each of Your Contributions are Your original + works of authorship (see Section 8 (Submissions on Behalf of + Others) for submission on behalf of others). You represent that to + Your knowledge, no other person claims, or has the right to claim, + any right in any intellectual property right related to Your + Contributions. + + You also represent that You are not legally obligated, whether by + entering into an agreement or otherwise, in any way that conflicts + with the terms of this Agreement. + + You represent that Your Contribution submissions include complete + details of any third-party license or other restriction (including, + but not limited to, related patents and trademarks) of which You + are personally aware and which are associated with any part of + Your Contributions. + + 6. You Don't Have an Obligation to Provide Support for Your Contributions + + You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. + + 6. No Warranties or Conditions + + StrongLoop acknowledges that unless required by applicable law or + agreed to in writing, You provide Your Contributions on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES + OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR + FITNESS FOR A PARTICULAR PURPOSE. + + 7. Submission on Behalf of Others + + If You wish to submit work that is not Your original creation, You + may submit it to StrongLoop separately from any Contribution, + identifying the complete details of its source and of any license + or other restriction (including, but not limited to, related + patents, trademarks, and license agreements) of which You are + personally aware, and conspicuously marking the work as + "Submitted on Behalf of a Third-Party: [named here]". + + 8. Agree to Notify of Change of Circumstances + + You agree to notify StrongLoop of any facts or circumstances of + which You become aware that would make these representations + inaccurate in any respect. Email us at callback@strongloop.com. +``` + +[Google C++ Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml +[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..16bb096 --- /dev/null +++ b/LICENSE @@ -0,0 +1,316 @@ +Copyright (c) 2013-2014 StrongLoop, Inc and other contributors. + +loopback uses a 'dual license' model. Users may use loopback under the terms of +the MIT license, or under the StrongLoop License. The text of both is included +below. + +MIT license + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +StrongLoop License + +You may obtain a copy of the License at + + http://www.strongloop.com/license/ + +STRONGLOOP SUBSCRIPTION AGREEMENT + +PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU +ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE +AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT +AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR +INSTALL OR USE THE SOFTWARE. +This StrongLoop Subscription Agreement ("Agreement") is made by and between +StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S. +B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this +Agreement ("Customer"). The effective date ("Effective Date") of this Agreement +is the date Customer agrees to these terms or installs or uses the Software (as +defined below). This Agreement applies to Customer's use of the Software but it +shall be superseded by any signed agreement between you and StrongLoop +concerning the Software. +1. Subscriptions and Licenses. +1.1 Subscriptions. StrongLoop offers five different subscription levels to its +customers, each as more particularly described on StrongLoop's website located +at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3) +Professional; (4) Gold; and (5) Platinum. The actual subscription level +applicable to Customer (the "Subscription") will be specified in the purchase +order that Customer issues to StrongLoop. This Agreement applies to Customer +regardless of the level of the Subscription selected by Customer and whether or +not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees +to provide the services as described on the StrongLoop Site for each +Subscription level during the term for which Customer has purchased the +applicable Subscription, subject to Customer paying the fees applicable to the +Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may +modify the services to be provided under any Subscription upon notice to +Customer. +1.2 License Grant. Subject to the terms and conditions of this Agreement, +StrongLoop grants to Customer, during the Subscription Term (as defined in +Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive, +non-transferable right and license, to install and use the StrongLoop Suite +software (the "Software") and the documentation made available electronically as +part of the Software (the "Documentation"), either of which may be modified +during the Term (as defined in Section 7.1 below), solely for development, +production and commercial purposes so long as Customer is using the Software to +run only one process on a given operating system at a time. This Agreement, +including but not limited to the license and restrictions contained herein, +apply to Customer regardless of whether Customer accesses the Software via +download from the StrongLoop Site or through a third-party website or service, +even if Customer acquired the Software prior to agreeing to this Agreement. +1.3 License Restrictions. Customer shall not itself, or through any parent, +subsidiary, affiliate, agent or other third party: + 1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer + in whole or in part, any Software or the Documentation to a third party; + or + 1.3.2 decompile, disassemble, translate, reverse engineer or otherwise + attempt to derive source code from the Software, in whole or in part, nor + shall Customer use any mechanical, electronic or other method to trace, + decompile, disassemble, or identify the source code of the Software or + encourage others to do so, except to the limited extent, if any, that + applicable law permits such acts notwithstanding any contractual + prohibitions, provided, however, before Customer exercises any rights that + Customer believes to be entitled to based on mandatory law, Customer shall + provide StrongLoop with thirty (30) days prior written notice and provide + all reasonably requested information to allow StrongLoop to assess + Customer's claim and, at StrongLoop's sole discretion, to provide + alternatives that reduce any adverse impact on StrongLoop's intellectual + property or other rights; or + 1.3.3 allow access or permit use of the Software by any users other than + Customer's employees or authorized third-party contractors who are + providing services to Customer and agree in writing to abide by the terms + of this Agreement, provided further that Customer shall be liable for any + failure by such employees and third-party contractors to comply with the + terms of this Agreement and no usage restrictions, if any, shall be + exceeded; or + 1.3.4 create, develop, license, install, use, or deploy any third party + software or services to circumvent or provide access, permissions or + rights which violate the license keys embedded within the Software; or + 1.3.5 modify or create derivative works based upon the Software or + Documentation; or disclose the results of any benchmark test of the + Software to any third party without StrongLoop's prior written approval; + or + 1.3.6 change any proprietary rights notices which appear in the Software + or Documentation; or + 1.3.7 use the Software as part of a time sharing or service bureau + purposes or in any other resale capacity. +1.4 Third-Party Software. The Software may include individual certain software +that is owned by third parties, including individual open source software +components (the "Third-Party Software"), each of which has its own copyright and +its own applicable license conditions. Such third-party software is licensed to +Customer under the terms of the applicable third-party licenses and/or copyright +notices that can be found in the LICENSES file, the Documentation or other +materials accompanying the Software, except that Sections 5 (Warranty +Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the +third-party software. Customer agrees to comply with the terms and conditions +of the relevant third-party software licenses. +2. Support Services. StrongLoop has no obligation to provide any support for +the Software other than the support services specifically described on the +StrongLoop Site for the Subscription level procured by Customer. However, +StrongLoop has endeavored to establish a community of users of the Software who +have provided their own feedback, hints and advice regarding their experiences +in using the Software. You can find that community and user feedback on the +StrongLoop Site. The use of any information, content or other materials from, +contained in or on the StrongLoop Site are subject to the StrongLoop website +terms of use located here http://www.strongloop.com/terms-of-service. +3. Confidentiality. For purposes of this Agreement, "Confidential Information" +means any and all information or proprietary materials (in every form and media) +not generally known in the relevant trade or industry and which has been or is +hereafter disclosed or made available by StrongLoop to Customer in connection +with the transactions contemplated under this Agreement, including (i) all trade +secrets, (ii) existing or contemplated Software, services, designs, technology, +processes, technical data, engineering, techniques, methodologies and concepts +and any related information, and (iii) information relating to business plans, +sales or marketing methods and customer lists or requirements. For a period of +five (5) years from the date of disclosure of the applicable Confidential +Information, Customer shall (i) hold the Confidential Information in trust and +confidence and avoid the disclosure or release thereof to any other person or +entity by using the same degree of care as it uses to avoid unauthorized use, +disclosure, or dissemination of its own Confidential Information of a similar +nature, but not less than reasonable care, and (ii) not use the Confidential +Information for any purpose whatsoever except as expressly contemplated under +this Agreement; provided that, to the extent the Confidential Information +constitutes a trade secret under law, Customer agrees to protect such +information for so long as it qualifies as a trade secret under applicable law. +Customer shall disclose the Confidential Information only to those of its +employees and contractors having a need to know such Confidential Information +and shall take all reasonable precautions to ensure that such employees and +contractors comply with the provisions of this Section. The obligations of +Customer under this Section shall not apply to information that Customer can +demonstrate (i) was in its possession at the time of disclosure and without +restriction as to confidentiality, (ii) at the time of disclosure is generally +available to the public or after disclosure becomes generally available to the +public through no breach of agreement or other wrongful act by Customer, (iii) +has been received from a third party without restriction on disclosure and +without breach of agreement by Customer, or (iv) is independently developed by +Customer without regard to the Confidential Information. In addition, Customer +may disclose Confidential Information as required to comply with binding orders +of governmental entities that have jurisdiction over it; provided that Customer +gives StrongLoop reasonable written notice to allow StrongLoop to seek a +protective order or other appropriate remedy, discloses only such Confidential +Information as is required by the governmental entity, and uses commercially +reasonable efforts to obtain confidential treatment for any Confidential +Information disclosed. Notwithstanding the above, Customer agrees that +StrongLoop, its employees and agents shall be free to use and employ their +general skills, know-how, and expertise, and to use, disclose, and employ any +generalized ideas, concepts, know-how, methods, techniques or skills gained or +learned during the Term or thereafter. +4. Ownership. StrongLoop shall retain all intellectual property and proprietary +rights in the Software, Documentation, and related works, including but not +limited to any derivative work of the foregoing and StrongLoop's licensors shall +retain all intellectual property and proprietary rights in any Third-Party +Software that may be provided with or as a part of the Software. Customer shall +do nothing inconsistent with StrongLoop's or its licensors' title to the +Software and the intellectual property rights embodied therein, including, but +not limited to, transferring, loaning, selling, assigning, pledging, or +otherwise disposing, encumbering, or suffering a lien or encumbrance upon or +against any interest in the Software. The Software (including any Third-Party +Software) contain copyrighted material, trade secrets and other proprietary +material of StrongLoop and/or its licensors. +5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND +DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP, +ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE, +PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE, +COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES +NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR +ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE +WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN +INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES +SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. +STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE, +BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW +THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO +CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE +SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN +THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR +FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS +SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS. +6. Limitation of Liability. + 6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS + BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE, + CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND + HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND + THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND + THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE + SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE + AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF + SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL + STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF + ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE) + ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE + HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER + TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM + ARISES. + 6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE + ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT. + THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT + BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING + LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT + PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL + PURPOSE. + 6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE + LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY + SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. + 6.4 Allocation of Risk. The sections on limitation of liability and + disclaimer of warranties allocate the risks in the Agreement between the + parties. This allocation is an essential element of the basis of the + bargain between the parties. +7. Term and Termination. +7.1 This Agreement shall commence on the Effective Date and continue for so long +as Customer has a valid Subscription and is current on the payment of any +Subscription Fees required to be paid for that Subscription (the "Subscription +Term"). Either party may terminate this Agreement immediately upon written +notice to the other party, and the Subscription and licenses granted hereunder +automatically terminate upon the termination of this Agreement. This Agreement +will terminate immediately without notice from StrongLoop if Customer fails to +comply with or otherwise breaches any provision of this Agreement. +7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall +survive the expiration or termination of this Agreement. +8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay +StrongLoop the Subscription Fees as described on the StrongLoop Site for the +Subscription purchased unless a different amount has been agreed to in a +separate agreement between Customer and StrongLoop. In addition, Customer shall +pay all sales, use, value added, withholding, excise taxes and other tax, duty, +custom and similar fees levied upon the delivery or use of the Software and the +Subscriptions described in this Agreement. Fees shall be invoiced in full upon +StrongLoop's acceptance of Customer's purchase order for the Subscription. All +invoices shall be paid in US dollars and are due upon receipt and shall be paid +within thirty (30) days. Payments shall be made without right of set-off or +chargeback. If Customer does not pay the invoices when due, StrongLoop may +charge interest at one percent (1%) per month or the highest rate permitted by +law, whichever is lower, on the unpaid balance from the original due date. If +Customer fails to pay fees in accordance with this Section, StrongLoop may +suspend fulfilling its obligations under this Agreement (including but not +limited to suspending the services under the Subscription) until payment is +received by StrongLoop. If any applicable law requires Customer to withhold +amounts from any payments to StrongLoop under this Agreement, (a) Customer shall +effect such withholding, remit such amounts to the appropriate taxing +authorities and promptly furnish StrongLoop with tax receipts evidencing the +payments of such amounts and (b) the sum payable by Customer upon which the +deduction or withholding is based shall be increased to the extent necessary to +ensure that, after such deduction or withholding, StrongLoop receives and +retains, free from liability for such deduction or withholding, a net amount +equal to the amount StrongLoop would have received and retained absent the +required deduction or withholding. +9. General. +9.1 Compliance with Laws. Customer shall abide by all local, state, federal and +international laws, rules, regulations and orders applying to Customer's use of +the Software, including, without limitation, the laws and regulations of the +United States that may restrict the export and re-export of certain commodities +and technical data of United States origin, including the Software. Customer +agrees that it will not export or re-export the Software without the appropriate +United States or foreign government licenses. +9.2 Entire Agreement. This Agreement constitutes the entire agreement between +the parties concerning the subject matter hereof. This Agreement supersedes all +prior or contemporaneous discussions, proposals and agreements between the +parties relating to the subject matter hereof. No amendment, modification or +waiver of any provision of this Agreement shall be effective unless in writing +and signed by both parties. Any additional or different terms on any purchase +orders issued by Customer to StrongLoop shall not be binding on either party, +are hereby rejected by StrongLoop and void. +9.3 Severability. If any provision of this Agreement is held to be invalid or +unenforceable, the remaining portions shall remain in full force and effect and +such provision shall be enforced to the maximum extent possible so as to effect +the intent of the parties and shall be reformed to the extent necessary to make +such provision valid and enforceable. +9.4 Waiver. No waiver of rights by either party may be implied from any actions +or failures to enforce rights under this Agreement. +9.5 Force Majeure. Neither party shall be liable to the other for any delay or +failure to perform due to causes beyond its reasonable control (excluding +payment of monies due). +9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the +terms of this Agreement are intended to be and are solely for the benefit of +StrongLoop and Customer and do not create any right in favor of any third party. +9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the +laws of the State of California, without reference to the principles of +conflicts of law. The provisions of the Uniform Computerized Information +Transaction Act and United Nations Convention on Contracts for the International +Sale of Goods shall not apply to this Agreement. The parties shall attempt to +resolve any dispute related to this Agreement informally, initially through +their respective management, and then by non-binding mediation in San Francisco +County, California. Any litigation related to this Agreement shall be brought +in the state or federal courts located in San Francisco County, California, and +only in those courts and each party irrevocably waives any objections to such +venue. +9.8 Notices. All notices must be in writing and shall be effective three (3) +days after the date sent to the other party's headquarters, Attention Chief +Financial Officer. diff --git a/README.md b/README.md index 9a55fd3..fbcfca6 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,26 @@ Hook into the various phases of a LoopBack application. ## Usage ```js -var loopback = require('loopback'); -var app = loopback(); +var PhaseList = require('loopback-phase').PhaseList; +var phases = new PhaseList(); +phases.add('first'); +phases.add('second'); +phases.add('third'); -app.phase('my-phase').after(function(cb) { - console.log('my-phase is complete'); +var first = phases.find('first'); +var second = phases.find('second'); + +first.use(function(ctx, cb) { + console.log('this is the first phase!'); + cb(); +}); + +second.use(function(ctx, cb) { + console.log('this is the second phase!'); cb(); }); -app.phase('my-phase').launch(); +phases.run(); ``` See [API docs](http://apidocs.strongloop.com/loopback-phase/) for diff --git a/docs.json b/docs.json new file mode 100644 index 0000000..7903fcd --- /dev/null +++ b/docs.json @@ -0,0 +1,6 @@ +{ + "content": [ + "lib/phase.js", + "lib/phase-list.js" + ] +} diff --git a/example/middleware.js b/example/middleware.js new file mode 100644 index 0000000..14ad491 --- /dev/null +++ b/example/middleware.js @@ -0,0 +1,24 @@ +var phaseOrder = [ + 'initial', + 'preprocess', + 'parse', + 'respond' +]; + +var express = require('express'); +var app = express(); +var phases = new PhaseList(); +phases.add(phaseOrder); + +app.use(function(req, res, next) { + phases.find('initial').run({ + req: req, + res: res + }, next); +}); + +phases.find('preprocess').use(function(ctx, cb) { + loopback.compress()(ctx.req, ctx.res, cb); +}); + +app.listen(3000); diff --git a/index.js b/index.js new file mode 100644 index 0000000..67483bb --- /dev/null +++ b/index.js @@ -0,0 +1,2 @@ +exports.PhaseList = require('./lib/phase-list'); +exports.Phase = require('./lib/phase'); diff --git a/lib/phase-list.js b/lib/phase-list.js new file mode 100644 index 0000000..10c0dba --- /dev/null +++ b/lib/phase-list.js @@ -0,0 +1,169 @@ +var Phase = require('./phase'); +var async = require('async'); + +module.exports = PhaseList; + +/** + * An ordered list of phases. + * + * ```js + * var PhaseList = require('loopback-phase').PhaseList; + * var phases = new PhaseList(); + * phases.add('my-phase'); + * ``` + * + * @class PhaseList + */ + +function PhaseList() { + this._phases = []; + this._phaseMap = {}; +} + +/** + * Get the first `Phase` in the list. + * + * @returns {Phase} The first phase. + */ + +PhaseList.prototype.first = function() { + return this._phases[0]; +} + +/** + * Get the last `Phase` in the list. + * + * @returns {Phase} The last phase. + */ + +PhaseList.prototype.last = function() { + this._phases[this._phases.length - 1]; +} + +/** + * Add one or more phases to the list. + * + * @param {Phase|String|String[]} phase The phase (or phases) to be added. + * @returns {Phase|Phase[]} The added phase or phases. + */ + +PhaseList.prototype.add = function(phase) { + var phaseList = this; + var phaseArray = Array.isArray(phase) ? phase : null; + + if(phaseArray) { + return phaseArray.map(phaseList.add.bind(phaseList)); + } + + if(typeof phase === 'string') { + phase = new Phase(phase); + } + + if(!phase.__isPhase__) throw new Error('Cannot add a non phase object to a PhaseList'); + + this._phases.push(phase); + this._phaseMap[phase.id] = phase; + + return phase; +} + +/** + * Remove a `Phase` from the list. + * + * @param {Phase|String} phase The phase to be removed. + * @returns {Phase} The removed phase. + */ + +PhaseList.prototype.remove = function(phase) { + var phases = this._phases; + var phaseMap = this._phaseMap; + var phaseId; + + if(!phase) return null; + + if(typeof phase === 'object') { + phaseId = phase.id; + } else { + phaseId = phase; + phase = phaseMap[phaseId]; + } + + if(!phase || !phase.__isPhase__) return null; + + phases.splice(phases.indexOf(phase), 1); + delete this._phaseMap[phaseId]; + + return phase; +} + +/** + * Find a `Phase` from the list. + * + * @param {String} id The phase identifier + * @returns {Phase} The `Phase` with the given `id`. + */ + +PhaseList.prototype.find = function(id) { + return this._phaseMap[id] || null; +} + +/** + * Find or add a `Phase` from/into the list. + * + * @param {String} id The phase identifier + * @returns {Phase} The `Phase` with the given `id`. + */ + +PhaseList.prototype.findOrAdd = function(id) { + var phase = this.find(id); + if(phase) return phase; + return this.add(id); +} + +/** + * Get the list of phases as an array of `Phase` objects. + * + * @returns {Phase[]} An array of phases. + */ + +PhaseList.prototype.toArray = function() { + return this._phases.slice(0); +} + +/** + * Launch the phases contained in the list. If there are no phases + * in the list `process.nextTick` is called with the provided callback. + * + * @param {Object} [context] The context of each `Phase` handler. + * @callback {Function} cb + * @param {Error} err Any error that occured during a phase contained + * in the list. + */ + +PhaseList.prototype.run = function(ctx, cb) { + var phases = this._phases; + + if(typeof ctx === 'function') { + cb = ctx; + ctx = undefined; + } + + if(phases.length) { + async.eachSeries(phases, function(ctx, next) { + phase.run(ctx, next); + }, cb); + } else { + process.nextTick(cb); + } +} + +/** + * Get an array of phase identifiers. + * @returns {String[]} phaseNames + */ + +PhaseList.prototype.getPhaseNames = function() { + return this._phases.map(function(phase) { + return phase.id; + }); +} diff --git a/lib/phase.js b/lib/phase.js new file mode 100644 index 0000000..aced0ab --- /dev/null +++ b/lib/phase.js @@ -0,0 +1,130 @@ +var async = require('async'); + +module.exports = Phase; + +/** + * A slice of time in an application. Provides hooks to allow + * functions to be executed before, during and after, the defined slice. + * + * ```js + * var Phase = require('loopback-phase').Phase; + * var myPhase = new Phase('my-phase'); + * ``` + * + * @class Phase + * + * @prop {Phase} next The `Phase` to be executed after this `Phase`. + * @prop {String} id The name or identifier of the `Phase`. + * + * @param {String} id The name or identifier of the `Phase`. + */ + +function Phase(id) { + this.id = id; + this.handlers = []; + this.beforeHandlers = []; + this.afterHandlers = []; +} + +/** + * Register a phase handler. The handler will be executed + * once the phase is launched. Handlers will be executed + * in parralel and must callback once complete. If the + * handler calls back with an error, the phase will immediately + * hault execution and call the callback provided to + * `phase.run(callback)`. + * + * **Example** + * + * ```js + * phase.use(function(ctx, next) { + * // specify an error if one occurred... + * var err = null; + * console.log(ctx.message, 'world!'); // => hello world + * next(err); + * }); + * + * phase.run({message: 'hello'}, function(err) { + * if(err) return console.error('phase has errored', err); + * console.log('phase has finished'); + * }); + * ``` + */ + +Phase.prototype.use = function(handler) { + this.handlers.push(handler); + return this; +} + +/** + * Register a phase handler to be executed before the phase begins. + * See `register()` for an example. + * + * @param {Function} handler + */ + +Phase.prototype.before = function(handler) { + this.beforeHandlers.push(handler); + return this; +} + +/** + * Register a phase handler to be executed after the phase completes. + * See `register()` for an example. + * + * @param {Function} handler + */ + +Phase.prototype.after = function(handler) { + this.afterHandlers.push(handler); + return this; +} + +/** + * Begin the execution of a phase and its handlers. Provide + * a context object to be passed as the first argument for each handler + * function. + * + * @param {Object} [context] The scope applied to each handler function. + * @callback {Function} callback + * @param {Error} err Any `Error` that occured during the execution of + * the phase. + */ + +Phase.prototype.run = function(ctx, cb) { + if(typeof ctx === 'function') { + cb = ctx; + ctx = {}; + } + + var steps = [ + async.apply(async.parallel, bind(this.beforeHandlers, ctx)), + async.apply(async.parallel, bind(this.handlers, ctx)), + async.apply(async.parallel, bind(this.afterHandlers, ctx)) + ]; + + async.series(steps, cb); +} + +function bind(handlers, ctx) { + return handlers.map(function(handler) { + return function(cb) { + handler(ctx, cb); + } + }); +} + +/** + * Return the `Phase` as a string. + */ + +Phase.prototype.toString = function() { + return this.id; +} + +// Internal flag to be used instead of +// `instanceof Phase` which breaks +// when there are two instances of +// `require('loopback-phase') + +Phase.prototype.__isPhase__ = true; diff --git a/package.json b/package.json new file mode 100644 index 0000000..bb5ecdb --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "loopback-phase", + "version": "0.0.0", + "description": "Hook into a LoopBack application's phases", + "keywords": [ + "StrongLoop", + "LoopBack", + "Hook", + "Phase" + ], + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-phase" + }, + "main": "index.js", + "scripts": { + "pretest": "jshint .", + "test": "mocha" + }, + "license": { + "name": "Dual MIT/StrongLoop", + "url": "https://github.com/strongloop/loopback-phase/blob/master/LICENSE" + }, + "dependencies": { + "debug": "^2.0.0", + "async": "^0.9.0" + }, + "devDependencies": { + "loopback": "^1.5.0", + "mocha": "^1.19.0", + "chai": "~1.9.2" + } +} diff --git a/test/phase-list.test.js b/test/phase-list.test.js new file mode 100644 index 0000000..ec0312c --- /dev/null +++ b/test/phase-list.test.js @@ -0,0 +1,106 @@ +var PhaseList = require('../').PhaseList; +var Phase = require('../').Phase; +var expect = require('chai').expect; + +describe('PhaseList', function() { + describe('phaseList.find(phaseName)', function() { + it('should find a phase by phaseName', function() { + var phaseList = new PhaseList(); + var phase = phaseList.add('test'); + expect(phase).to.eql(phaseList.find('test')); + }); + }); + + describe('phaseList.findOrAdd(phaseName)', function() { + it('should always return a phase', function() { + var phaseList = new PhaseList(); + var randomKey = Math.random().toString(); + var phase = phaseList.findOrAdd(randomKey); + expect(phase.id).to.equal(randomKey); + }); + }); + + describe('phaseList.add(phaseName)', function() { + it('should add a phase to the list', function() { + var phase = new Phase('myPhase'); + var phases = new PhaseList(); + phases.add(phase); + var result = phases.find('myPhase'); + expect(result).to.equal(phase); + }); + it('should create a phase and add it to the list', function() { + var phases = new PhaseList(); + phases.add('myPhase'); + var result = phases.find('myPhase'); + expect(result.id).to.equal('myPhase'); + }); + it('should create and add an array oh phases', function() { + var phases = new PhaseList(); + phases.add(['foo', 'bar']); + var foo = phases.find('foo'); + var bar = phases.find('bar'); + expect(foo.id).to.equal('foo'); + expect(bar.id).to.equal('bar'); + }); + }); + + describe('phaseList.remove(phaseName)', function() { + it('should remove a phase from the list', function() { + var phase = new Phase('myPhase'); + var phases = new PhaseList(); + phases.add(phase); + var result = phases.find('myPhase'); + expect(result).to.equal(phase); + phases.remove(phase.id); + expect(phases.find('myPhase')).to.equal(null); + }); + + it('should not remove any phase if phase is not in the list', function() { + var phase = new Phase('myPhase'); + var phases = new PhaseList(); + phases.add('bar'); + var result = phases.find('myPhase'); + expect(result).to.equal(null); + var removed = phases.remove(phase.id); + expect(removed).to.equal(null); + expect(phases.getPhaseNames()).to.eql(['bar']); + }); + }); + + describe('phases.toArray()', function() { + it('should return the list of phases as an array', function() { + var names = ['a', 'b']; + var phases = new PhaseList(); + + phases.add(names); + + var result = phases + .toArray() + .map(function(phase) { + return phase.id; + }); + + expect(names).to.eql(result); + }); + }); + + describe('phaseList.run(ctx, cb)', function() { + var phases = new PhaseList(); + var phase = phases.add('foo'); + var called = false; + phase.use(function(ctx, cb) { + called = true; + expect(ctx.hello).to.equal('world'); + cb(); + }); + var phases = new PhaseList(); + phases.run({hello: 'world'}, function(err) { + expect(called).to.equal(true); + done(); + }); + }); + + describe('phaseList.getPhaseNames()', function() { + + }) +}); diff --git a/test/phase.test.js b/test/phase.test.js new file mode 100644 index 0000000..c93a272 --- /dev/null +++ b/test/phase.test.js @@ -0,0 +1,64 @@ +var assert = require('assert'); +var expect = require('chai').expect; +var Phase = require('../').Phase; + +describe('Phase', function() { + describe('phase.run(ctx, cb)', function() { + it('should execute phase handlers', function (done) { + var phase = new Phase(); + var called = false; + phase.use(function(ctx, cb) { + called = true; + cb(); + }); + phase.run(function() { + assert(called === true); + done(); + }); + }); + + it('should set the context for handlers', function (done) { + var phase = new Phase(); + phase.use(function(ctx, cb) { + expect(ctx).to.have.property('foo', 'bar'); + cb(); + }); + phase.run({foo: 'bar'}, done); + }); + }); + + describe('phase.use(handler)', function() { + it('should add a handler that is invoked during a phase', function (done) { + var phase = new Phase(); + var invoked = false; + phase.use(function(ctx, cb) { + invoked = true; + cb(); + }); + phase.run(function() { + expect(invoked).to.equal(true); + done(); + }); + }); + }); + + describe('phase.after(handler)', function() { + it('should add a handler that is invoked after a phase', function (done) { + var phase = new Phase('test'); + phase + .use(function(ctx, cb) { + ctx.foo = 'ba'; + cb(); + }) + .use(function(ctx, cb) { + ctx.foo = ctx.foo + 'r'; + cb(); + }); + phase.after(function(ctx, cb) { + assert(ctx.foo === 'bar'); + cb(); + }); + phase.run(done); + }); + }); +});