From d7eca3b2f44ba682322486f6f47257b075956f0e Mon Sep 17 00:00:00 2001 From: michael shanks Date: Mon, 15 Jul 2019 07:12:52 +0100 Subject: [PATCH] added core into mono repo --- packages/core/.babelrc | 12 + packages/core/.eslintrc.json | 11 + packages/core/.gitignore | 55 + packages/core/.travis.yml | 11 + packages/core/.vscode/launch.json | 14 + packages/core/.vscode/settings.json | 0 packages/core/AUTHORS.md | 5 + packages/core/CONTRIBUTING.md | 22 + packages/core/LICENSE | 373 + packages/core/package-lock.json | 9928 +++++++++++++++++ packages/core/package.json | 83 + packages/core/q | 1131 ++ packages/core/readme.md | 32 + packages/core/rollup.config.js | 76 + .../src/actionsApi/buildBehaviourSource.js | 15 + packages/core/src/actionsApi/execute.js | 15 + packages/core/src/actionsApi/index.js | 7 + packages/core/src/actionsApi/initialise.js | 74 + .../core/src/appInitialise/databaseManager.js | 27 + .../core/src/appInitialise/eventAggregator.js | 27 + .../core/src/appInitialise/inProcIndexer.js | 0 packages/core/src/appInitialise/index.js | 36 + .../core/src/appInitialise/initialiseData.js | 58 + packages/core/src/authApi/authCommon.js | 49 + packages/core/src/authApi/authenticate.js | 117 + .../core/src/authApi/createTemporaryAccess.js | 79 + packages/core/src/authApi/createUser.js | 96 + packages/core/src/authApi/enableUser.js | 49 + .../src/authApi/generateFullPermissions.js | 45 + .../core/src/authApi/getNewAccessLevel.js | 5 + packages/core/src/authApi/getNewUser.js | 31 + packages/core/src/authApi/getUsers.js | 19 + packages/core/src/authApi/index.js | 48 + packages/core/src/authApi/isAuthorized.js | 50 + packages/core/src/authApi/loadAccessLevels.js | 13 + packages/core/src/authApi/permissions.js | 74 + packages/core/src/authApi/saveAccessLevels.js | 49 + packages/core/src/authApi/setPassword.js | 153 + .../core/src/authApi/setUserAccessLevels.js | 49 + .../core/src/authApi/validateAccessLevels.js | 75 + packages/core/src/authApi/validateUser.js | 39 + packages/core/src/collectionApi/delete.js | 79 + .../collectionApi/getAllowedRecordTypes.js | 19 + packages/core/src/collectionApi/index.js | 11 + packages/core/src/collectionApi/initialise.js | 61 + packages/core/src/common/apiWrapper.js | 116 + packages/core/src/common/compileCode.js | 30 + packages/core/src/common/errors.js | 34 + packages/core/src/common/events.js | 93 + packages/core/src/common/index.js | 265 + packages/core/src/common/lock.js | 99 + packages/core/src/common/validationCommon.js | 17 + packages/core/src/index.js | 113 + packages/core/src/indexApi/aggregates.js | 168 + packages/core/src/indexApi/buildIndex.js | 198 + packages/core/src/indexApi/delete.js | 43 + packages/core/src/indexApi/index.js | 11 + packages/core/src/indexApi/listItems.js | 66 + packages/core/src/indexing/allIds.js | 229 + packages/core/src/indexing/apply.js | 79 + packages/core/src/indexing/evaluate.js | 98 + .../core/src/indexing/indexSchemaCreator.js | 58 + packages/core/src/indexing/initialiseIndex.js | 22 + .../src/indexing/promiseReadableStream.js | 84 + .../src/indexing/promiseWritableStream.js | 120 + packages/core/src/indexing/read.js | 99 + packages/core/src/indexing/relevant.js | 81 + packages/core/src/indexing/serializer.js | 252 + packages/core/src/indexing/sharding.js | 124 + packages/core/src/indexing/sweeper.js | 0 packages/core/src/recordApi/customId.js | 29 + packages/core/src/recordApi/delete.js | 96 + packages/core/src/recordApi/downloadFile.js | 24 + packages/core/src/recordApi/getContext.js | 75 + packages/core/src/recordApi/getNew.js | 44 + packages/core/src/recordApi/index.js | 28 + packages/core/src/recordApi/load.js | 70 + packages/core/src/recordApi/save.js | 184 + packages/core/src/recordApi/uploadFile.js | 128 + packages/core/src/recordApi/validate.js | 85 + .../core/src/templateApi/createActions.js | 23 + packages/core/src/templateApi/createNodes.js | 223 + packages/core/src/templateApi/deleteNodes.js | 12 + packages/core/src/templateApi/fields.js | 87 + .../templateApi/getApplicationDefinition.js | 14 + .../src/templateApi/getBehaviourSources.js | 4 + packages/core/src/templateApi/hierarchy.js | 234 + packages/core/src/templateApi/index.js | 64 + packages/core/src/templateApi/indexes.js | 38 + .../src/templateApi/recordValidationRules.js | 37 + .../src/templateApi/saveActionsAndTriggers.js | 40 + .../templateApi/saveApplicationHierarchy.js | 35 + packages/core/src/templateApi/validate.js | 183 + .../core/src/templateApi/validateAggregate.js | 25 + packages/core/src/transactions/cleanup.js | 47 + packages/core/src/transactions/create.js | 69 + packages/core/src/transactions/execute.js | 349 + packages/core/src/transactions/retrieve.js | 206 + .../src/transactions/transactionsCommon.js | 48 + packages/core/src/types/array.js | 59 + packages/core/src/types/bool.js | 40 + packages/core/src/types/datetime.js | 62 + packages/core/src/types/file.js | 58 + packages/core/src/types/index.js | 75 + packages/core/src/types/number.js | 73 + packages/core/src/types/object.js | 52 + packages/core/src/types/reference.js | 74 + packages/core/src/types/string.js | 62 + packages/core/src/types/typeHelpers.js | 71 + packages/core/test/actionsApi.execute.spec.js | 95 + packages/core/test/apiWrapper.spec.js | 166 + .../core/test/authApi.authenticate.spec.js | 127 + .../core/test/authApi.changePassword.spec.js | 123 + .../test/authApi.createAccessLevels.spec.js | 190 + packages/core/test/authApi.createUser.spec.js | 190 + .../core/test/authApi.disableUser.spec.js | 134 + .../test/authApi.setUserAccesslLevels.spec.js | 68 + .../core/test/collectionApi.delete.spec.js | 71 + packages/core/test/collectionApi.spec.js | 191 + packages/core/test/common.spec.js | 257 + packages/core/test/getApis.spec.js | 86 + .../core/test/indexApi.aggregates.spec.js | 149 + .../core/test/indexApi.buildIndex.spec.js | 639 ++ packages/core/test/indexApi.list.spec.js | 155 + .../core/test/indexing.concurrency.spec.js | 269 + .../test/indexing.createIndexFile.spec.js | 21 + packages/core/test/indexing.evaluate.spec.js | 138 + .../test/indexing.getRelevantIndexes.spec.js | 143 + packages/core/test/indexing.schema.spec.js | 132 + packages/core/test/initialiseData.spec.js | 74 + packages/core/test/memory.js | 135 + packages/core/test/nodeCrypto.js | 5 + packages/core/test/recordApi.customId.spec.js | 34 + packages/core/test/recordApi.delete.spec.js | 64 + packages/core/test/recordApi.files.spec.js | 98 + .../core/test/recordApi.getContext.spec.js | 94 + packages/core/test/recordApi.reindex.spec.js | 597 + packages/core/test/recordApi.spec.js | 348 + packages/core/test/recordApi.validate.spec.js | 268 + packages/core/test/specHelpers.js | 507 + packages/core/test/support/jasmine.json | 11 + .../templateApi.actionsValidation.spec.js | 120 + .../templateApi.constructHeirarchy.spec.js | 171 + packages/core/test/templateApi.fields.spec.js | 117 + .../templateApi.heirarchyValidation.spec.js | 396 + .../templateApi.loadSaveHeirarchy.spec.js | 176 + packages/core/test/templateApi.types.spec.js | 229 + packages/core/yarn.lock | 6357 +++++++++++ 148 files changed, 31993 insertions(+) create mode 100644 packages/core/.babelrc create mode 100644 packages/core/.eslintrc.json create mode 100644 packages/core/.gitignore create mode 100644 packages/core/.travis.yml create mode 100644 packages/core/.vscode/launch.json create mode 100644 packages/core/.vscode/settings.json create mode 100644 packages/core/AUTHORS.md create mode 100644 packages/core/CONTRIBUTING.md create mode 100644 packages/core/LICENSE create mode 100644 packages/core/package-lock.json create mode 100644 packages/core/package.json create mode 100644 packages/core/q create mode 100644 packages/core/readme.md create mode 100644 packages/core/rollup.config.js create mode 100644 packages/core/src/actionsApi/buildBehaviourSource.js create mode 100644 packages/core/src/actionsApi/execute.js create mode 100644 packages/core/src/actionsApi/index.js create mode 100644 packages/core/src/actionsApi/initialise.js create mode 100644 packages/core/src/appInitialise/databaseManager.js create mode 100644 packages/core/src/appInitialise/eventAggregator.js create mode 100644 packages/core/src/appInitialise/inProcIndexer.js create mode 100644 packages/core/src/appInitialise/index.js create mode 100644 packages/core/src/appInitialise/initialiseData.js create mode 100644 packages/core/src/authApi/authCommon.js create mode 100644 packages/core/src/authApi/authenticate.js create mode 100644 packages/core/src/authApi/createTemporaryAccess.js create mode 100644 packages/core/src/authApi/createUser.js create mode 100644 packages/core/src/authApi/enableUser.js create mode 100644 packages/core/src/authApi/generateFullPermissions.js create mode 100644 packages/core/src/authApi/getNewAccessLevel.js create mode 100644 packages/core/src/authApi/getNewUser.js create mode 100644 packages/core/src/authApi/getUsers.js create mode 100644 packages/core/src/authApi/index.js create mode 100644 packages/core/src/authApi/isAuthorized.js create mode 100644 packages/core/src/authApi/loadAccessLevels.js create mode 100644 packages/core/src/authApi/permissions.js create mode 100644 packages/core/src/authApi/saveAccessLevels.js create mode 100644 packages/core/src/authApi/setPassword.js create mode 100644 packages/core/src/authApi/setUserAccessLevels.js create mode 100644 packages/core/src/authApi/validateAccessLevels.js create mode 100644 packages/core/src/authApi/validateUser.js create mode 100644 packages/core/src/collectionApi/delete.js create mode 100644 packages/core/src/collectionApi/getAllowedRecordTypes.js create mode 100644 packages/core/src/collectionApi/index.js create mode 100644 packages/core/src/collectionApi/initialise.js create mode 100644 packages/core/src/common/apiWrapper.js create mode 100644 packages/core/src/common/compileCode.js create mode 100644 packages/core/src/common/errors.js create mode 100644 packages/core/src/common/events.js create mode 100644 packages/core/src/common/index.js create mode 100644 packages/core/src/common/lock.js create mode 100644 packages/core/src/common/validationCommon.js create mode 100644 packages/core/src/index.js create mode 100644 packages/core/src/indexApi/aggregates.js create mode 100644 packages/core/src/indexApi/buildIndex.js create mode 100644 packages/core/src/indexApi/delete.js create mode 100644 packages/core/src/indexApi/index.js create mode 100644 packages/core/src/indexApi/listItems.js create mode 100644 packages/core/src/indexing/allIds.js create mode 100644 packages/core/src/indexing/apply.js create mode 100644 packages/core/src/indexing/evaluate.js create mode 100644 packages/core/src/indexing/indexSchemaCreator.js create mode 100644 packages/core/src/indexing/initialiseIndex.js create mode 100644 packages/core/src/indexing/promiseReadableStream.js create mode 100644 packages/core/src/indexing/promiseWritableStream.js create mode 100644 packages/core/src/indexing/read.js create mode 100644 packages/core/src/indexing/relevant.js create mode 100644 packages/core/src/indexing/serializer.js create mode 100644 packages/core/src/indexing/sharding.js create mode 100644 packages/core/src/indexing/sweeper.js create mode 100644 packages/core/src/recordApi/customId.js create mode 100644 packages/core/src/recordApi/delete.js create mode 100644 packages/core/src/recordApi/downloadFile.js create mode 100644 packages/core/src/recordApi/getContext.js create mode 100644 packages/core/src/recordApi/getNew.js create mode 100644 packages/core/src/recordApi/index.js create mode 100644 packages/core/src/recordApi/load.js create mode 100644 packages/core/src/recordApi/save.js create mode 100644 packages/core/src/recordApi/uploadFile.js create mode 100644 packages/core/src/recordApi/validate.js create mode 100644 packages/core/src/templateApi/createActions.js create mode 100644 packages/core/src/templateApi/createNodes.js create mode 100644 packages/core/src/templateApi/deleteNodes.js create mode 100644 packages/core/src/templateApi/fields.js create mode 100644 packages/core/src/templateApi/getApplicationDefinition.js create mode 100644 packages/core/src/templateApi/getBehaviourSources.js create mode 100644 packages/core/src/templateApi/hierarchy.js create mode 100644 packages/core/src/templateApi/index.js create mode 100644 packages/core/src/templateApi/indexes.js create mode 100644 packages/core/src/templateApi/recordValidationRules.js create mode 100644 packages/core/src/templateApi/saveActionsAndTriggers.js create mode 100644 packages/core/src/templateApi/saveApplicationHierarchy.js create mode 100644 packages/core/src/templateApi/validate.js create mode 100644 packages/core/src/templateApi/validateAggregate.js create mode 100644 packages/core/src/transactions/cleanup.js create mode 100644 packages/core/src/transactions/create.js create mode 100644 packages/core/src/transactions/execute.js create mode 100644 packages/core/src/transactions/retrieve.js create mode 100644 packages/core/src/transactions/transactionsCommon.js create mode 100644 packages/core/src/types/array.js create mode 100644 packages/core/src/types/bool.js create mode 100644 packages/core/src/types/datetime.js create mode 100644 packages/core/src/types/file.js create mode 100644 packages/core/src/types/index.js create mode 100644 packages/core/src/types/number.js create mode 100644 packages/core/src/types/object.js create mode 100644 packages/core/src/types/reference.js create mode 100644 packages/core/src/types/string.js create mode 100644 packages/core/src/types/typeHelpers.js create mode 100644 packages/core/test/actionsApi.execute.spec.js create mode 100644 packages/core/test/apiWrapper.spec.js create mode 100644 packages/core/test/authApi.authenticate.spec.js create mode 100644 packages/core/test/authApi.changePassword.spec.js create mode 100644 packages/core/test/authApi.createAccessLevels.spec.js create mode 100644 packages/core/test/authApi.createUser.spec.js create mode 100644 packages/core/test/authApi.disableUser.spec.js create mode 100644 packages/core/test/authApi.setUserAccesslLevels.spec.js create mode 100644 packages/core/test/collectionApi.delete.spec.js create mode 100644 packages/core/test/collectionApi.spec.js create mode 100644 packages/core/test/common.spec.js create mode 100644 packages/core/test/getApis.spec.js create mode 100644 packages/core/test/indexApi.aggregates.spec.js create mode 100644 packages/core/test/indexApi.buildIndex.spec.js create mode 100644 packages/core/test/indexApi.list.spec.js create mode 100644 packages/core/test/indexing.concurrency.spec.js create mode 100644 packages/core/test/indexing.createIndexFile.spec.js create mode 100644 packages/core/test/indexing.evaluate.spec.js create mode 100644 packages/core/test/indexing.getRelevantIndexes.spec.js create mode 100644 packages/core/test/indexing.schema.spec.js create mode 100644 packages/core/test/initialiseData.spec.js create mode 100644 packages/core/test/memory.js create mode 100644 packages/core/test/nodeCrypto.js create mode 100644 packages/core/test/recordApi.customId.spec.js create mode 100644 packages/core/test/recordApi.delete.spec.js create mode 100644 packages/core/test/recordApi.files.spec.js create mode 100644 packages/core/test/recordApi.getContext.spec.js create mode 100644 packages/core/test/recordApi.reindex.spec.js create mode 100644 packages/core/test/recordApi.spec.js create mode 100644 packages/core/test/recordApi.validate.spec.js create mode 100644 packages/core/test/specHelpers.js create mode 100644 packages/core/test/support/jasmine.json create mode 100644 packages/core/test/templateApi.actionsValidation.spec.js create mode 100644 packages/core/test/templateApi.constructHeirarchy.spec.js create mode 100644 packages/core/test/templateApi.fields.spec.js create mode 100644 packages/core/test/templateApi.heirarchyValidation.spec.js create mode 100644 packages/core/test/templateApi.loadSaveHeirarchy.spec.js create mode 100644 packages/core/test/templateApi.types.spec.js create mode 100644 packages/core/yarn.lock diff --git a/packages/core/.babelrc b/packages/core/.babelrc new file mode 100644 index 0000000000..c52b9d492a --- /dev/null +++ b/packages/core/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": ["@babel/preset-env"], + "sourceMaps": "inline", + "retainLines": true, + "plugins": [ + ["@babel/plugin-transform-runtime", + { + "regenerator": true + } + ] + ] +} \ No newline at end of file diff --git a/packages/core/.eslintrc.json b/packages/core/.eslintrc.json new file mode 100644 index 0000000000..24c308306e --- /dev/null +++ b/packages/core/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "eslint:recommended", + "parser": "babel-eslint", + "parserOptions": { + "sourceType": "module" + } +} \ No newline at end of file diff --git a/packages/core/.gitignore b/packages/core/.gitignore new file mode 100644 index 0000000000..d06ac42577 --- /dev/null +++ b/packages/core/.gitignore @@ -0,0 +1,55 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release +.eslintcache + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules +node_modules_ubuntu +node_modules_windows +app/node_modules + +# OSX +.DS_Store + +# flow-typed +flow-typed/npm/* +!flow-typed/npm/module_vx.x.x.js + +# App packaged +release +app/main.prod.js +app/main.prod.js.map +app/renderer.prod.js +app/renderer.prod.js.map +app/style.css +app/style.css.map +dist +dist-test +dll +main.js +main.js.map + +.idea +npm-debug.log.* diff --git a/packages/core/.travis.yml b/packages/core/.travis.yml new file mode 100644 index 0000000000..56e1192d3b --- /dev/null +++ b/packages/core/.travis.yml @@ -0,0 +1,11 @@ +sudo: required + +notifications: + slack: budibase:Nx2QNi9CP87Nn7ah2A4Qdzyy + +script: +- npm install +- npm install -g jest +- node node_modules/eslint/bin/eslint src/**/*.js +- jest + diff --git a/packages/core/.vscode/launch.json b/packages/core/.vscode/launch.json new file mode 100644 index 0000000000..b048bd4f3d --- /dev/null +++ b/packages/core/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}\\index.js" + } + ] +} \ No newline at end of file diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/AUTHORS.md b/packages/core/AUTHORS.md new file mode 100644 index 0000000000..72ae91578d --- /dev/null +++ b/packages/core/AUTHORS.md @@ -0,0 +1,5 @@ +Contributors +=== + +* Michael Shanks - [@mikebudi](https://github.com/mjashanks) +* Daniel Loudon - [@danbudi](https://github.com/danbudi) diff --git a/packages/core/CONTRIBUTING.md b/packages/core/CONTRIBUTING.md new file mode 100644 index 0000000000..4d1ae8559d --- /dev/null +++ b/packages/core/CONTRIBUTING.md @@ -0,0 +1,22 @@ +### Contributing to budibase-core + +* The contributors are listed in [AUTHORS.md](https://github.com/budibase/budibase-core/blob/master/AUTHORS.md) (add yourself). + +* This project uses a modified version of the MPLv2 license, see [LICENSE](https://github.com/budibase/budibase-core/blob/master/LICENSE). + +* 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/core/LICENSE b/packages/core/LICENSE new file mode 100644 index 0000000000..a612ad9813 --- /dev/null +++ b/packages/core/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json new file mode 100644 index 0000000000..aebe138ef4 --- /dev/null +++ b/packages/core/package-lock.json @@ -0,0 +1,9928 @@ +{ + "name": "budibase-core", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/cli": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.4.4.tgz", + "integrity": "sha512-XGr5YjQSjgTa6OzQZY57FAJsdeVSAKR/u/KA5exWIz66IKtv/zXtHy+fIZcMry/EgYegwuHE7vzGnrFhjdIAsQ==", + "dev": true, + "requires": { + "chokidar": "^2.0.4", + "commander": "^2.8.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "lodash": "^4.17.11", + "mkdirp": "^0.5.1", + "output-file-sync": "^2.0.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", + "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helpers": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.5", + "@babel/types": "^7.4.4", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "requires": { + "@babel/types": "^7.4.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-define-map": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz", + "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", + "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", + "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", + "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", + "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", + "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==" + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.2.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", + "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", + "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", + "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", + "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.11" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", + "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", + "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", + "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", + "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", + "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", + "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", + "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", + "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", + "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", + "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", + "dev": true, + "requires": { + "regexp-tree": "^0.1.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", + "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", + "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", + "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz", + "integrity": "sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", + "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/preset-env": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.5.tgz", + "integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.4.4", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.4.4", + "@babel/plugin-transform-classes": "^7.4.4", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.4.4", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-modules-systemjs": "^7.4.4", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", + "@babel/plugin-transform-new-target": "^7.4.4", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.4.4", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.4.5", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + } + }, + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + } + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@jest/console": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", + "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "dev": true, + "requires": { + "@jest/source-map": "^24.3.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", + "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.8.0", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve-dependencies": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "jest-watcher": "^24.8.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "@jest/environment": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", + "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/fake-timers": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", + "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/reporters": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", + "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.1.1", + "jest-haste-map": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", + "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/types": "^24.8.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", + "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", + "dev": true, + "requires": { + "@jest/test-result": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0" + } + }, + "@jest/transform": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", + "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.8.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-util": "^24.8.0", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "babel-plugin-istanbul": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", + "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + } + } + }, + "@jest/types": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", + "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^12.0.9" + } + }, + "@nx-js/compiler-util": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz", + "integrity": "sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ==" + }, + "@phc/format": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-0.5.0.tgz", + "integrity": "sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2" + } + }, + "@types/babel__core": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", + "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/node": { + "version": "12.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz", + "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==", + "dev": true + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/yargs": { + "version": "12.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", + "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", + "dev": true + }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "dev": true + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "argon2": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.20.1.tgz", + "integrity": "sha512-ds6SU6YAXoJQGgc9tMOfb55Dyls+b3oaY9bSED0/O83aqlBOEEKR+mbmrR37MmlGaDqKrGDfWoTlHUqeZw8sHQ==", + "dev": true, + "requires": { + "@phc/format": "^0.5.0", + "bindings": "^1.3.0", + "node-addon-api": "^1.6.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-eslint": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.2.tgz", + "integrity": "sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + } + } + }, + "babel-jest": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz", + "integrity": "sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==", + "dev": true, + "requires": { + "babel-plugin-istanbul": "^4.1.6", + "babel-preset-jest": "^23.2.0" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-istanbul": { + "version": "4.1.6", + "resolved": "http://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", + "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "find-up": "^2.1.0", + "istanbul-lib-instrument": "^1.10.1", + "test-exclude": "^4.2.1" + } + }, + "babel-plugin-jest-hoist": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz", + "integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-jest": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz", + "integrity": "sha1-jsegOhOPABoaj7HoETZSvxpV2kY=", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^23.2.0", + "babel-plugin-syntax-object-rest-spread": "^6.13.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserslist": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.2.tgz", + "integrity": "sha512-2neU/V0giQy9h3XMPwLhEY3+Ao0uHSwHvU8Q1Ea6AgLVL1sXbX3dzPrJ8NWe5Hi4PoTkCYXOtVR9rfRLI0J/8Q==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000974", + "electron-to-chromium": "^1.3.150", + "node-releases": "^1.1.23" + } + }, + "bser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", + "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + }, + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000974", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000974.tgz", + "integrity": "sha512-xc3rkNS/Zc3CmpMKuczWEdY2sZgx09BkAxfvkxlAEBTqcMHeL8QnPqhKse+5sRTi3nrw2pJwToD2WvKn1Uhvww==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "chokidar": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "optional": true + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "dev": true + }, + "core-js-compat": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.3.tgz", + "integrity": "sha512-EP018pVhgwsKHz3YoN1hTq49aRe+h017Kjz0NQz3nXV0cCRMvH3fLQl+vEPGr4r4J5sk4sU3tUC7U1aqTCeJeA==", + "dev": true, + "requires": { + "browserslist": "^4.6.0", + "core-js-pure": "3.1.3", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + } + } + }, + "core-js-pure": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.3.tgz", + "integrity": "sha512-k3JWTrcQBKqjkjI0bkfXS0lbpWPxYuHWfMMjC1VDmzU4Q58IwSbuXSo99YO/hUHlw/EB4AlfA2PVxOGkrIq6dA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-env": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", + "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "is-windows": "^1.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "dev": true + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "damerau-levenshtein": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", + "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "date-fns": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", + "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diff-sequences": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", + "dev": true + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron-to-chromium": { + "version": "1.3.156", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.156.tgz", + "integrity": "sha512-mQOd8SAYDoIYRfTS/sr9rjX+PjBdAKU62vzS7CVfVx62KG5GRCWMz0c5Ebt0cb2kPFdl5UVNMXa8eSZt3VHvBw==", + "dev": true + }, + "emoji-regex": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", + "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", + "dev": true, + "requires": { + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^4.0.0", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.2", + "imurmurhash": "^0.1.4", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.5.0", + "string.prototype.matchall": "^2.0.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "eslint-config-airbnb": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.1.0.tgz", + "integrity": "sha512-R9jw28hFfEQnpPau01NO5K/JWMGLi6aymiF6RsnMURjTk+MqZKllCqGK/0tOvHkPi/NWSSOU2Ced/GX++YxLnw==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^13.1.0", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4" + } + }, + "eslint-config-airbnb-base": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz", + "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==", + "dev": true, + "requires": { + "eslint-restricted-globals": "^0.1.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + } + }, + "eslint-module-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", + "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", + "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", + "dev": true, + "requires": { + "contains-path": "^0.1.0", + "debug": "^2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.2.0", + "has": "^1.0.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0", + "resolve": "^1.6.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.1.tgz", + "integrity": "sha512-JsxNKqa3TwmPypeXNnI75FntkUktGzI1wSa1LgNZdSOMI+B4sxnr1lSF8m8lPiz4mKiC+14ysZQM4scewUrP7A==", + "dev": true, + "requires": { + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.1", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^6.5.1", + "has": "^1.0.3", + "jsx-ast-utils": "^2.0.1" + } + }, + "eslint-plugin-react": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.0.tgz", + "integrity": "sha512-SJOh2p3Mr1nbp/Nd5odTuSn2rvaMvO5DaOpuAGc9Sc+Gcxqkyffb1mqQGIKB9tWQJlvrfsrzWnMJexZJ7YRDUw==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.0.1", + "prop-types": "^15.6.2" + } + }, + "eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + } + }, + "expect": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", + "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true, + "requires": { + "bser": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-reference": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.2.tgz", + "integrity": "sha512-Kn5g8c7XHKejFOpTf2QN9YjiHHKl5xRj+2uAZf9iM2//nkBNi/NNeB5JMoun28nEaUVHyPUzqzhfRlfAirEjXg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", + "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.8.0" + }, + "dependencies": { + "jest-cli": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "dev": true, + "requires": { + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + } + } + }, + "jest-changed-files": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", + "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", + "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.8.0", + "@jest/types": "^24.8.0", + "babel-jest": "^24.8.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.8.0", + "jest-environment-node": "^24.8.0", + "jest-get-type": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.8.0", + "realpath-native": "^1.1.0" + }, + "dependencies": { + "babel-jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", + "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", + "dev": true, + "requires": { + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.6.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", + "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", + "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", + "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.6.0" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + } + } + }, + "jest-diff": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", + "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-docblock": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", + "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", + "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-environment-jsdom": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", + "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", + "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0" + } + }, + "jest-get-type": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", + "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", + "dev": true + }, + "jest-haste-map": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.0.tgz", + "integrity": "sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "jest-jasmine2": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", + "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.8.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", + "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "dev": true, + "requires": { + "pretty-format": "^24.8.0" + } + }, + "jest-matcher-utils": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", + "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.8.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-message-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", + "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "jest-mock": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", + "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", + "dev": true + }, + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", + "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.8.0" + } + }, + "jest-runner": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", + "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-leak-detector": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + } + } + }, + "jest-runtime": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", + "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.2", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^12.0.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "jest-serializer": { + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", + "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", + "dev": true + }, + "jest-snapshot": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", + "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "expect": "^24.8.0", + "jest-diff": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.8.0", + "semver": "^5.5.0" + } + }, + "jest-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", + "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/fake-timers": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "jest-validate": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", + "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "camelcase": "^5.0.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "leven": "^2.1.0", + "pretty-format": "^24.8.0" + } + }, + "jest-watcher": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", + "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.9", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.8.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", + "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "dev": true, + "requires": { + "merge-stream": "^1.0.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz", + "integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lunr": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.5.tgz", + "integrity": "sha512-EtnfmHsHJTr3u24sito9JctSxej5Ds0SgUD2Lm+qRHyLgM7BGesFlW14eNh1mil0fV5Muh8gf3dBBXzADlUlzQ==" + }, + "magic-string": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz", + "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + } + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.0.tgz", + "integrity": "sha512-OP8SoC91Kyjl1sdSTEnM1xYh4gUEOSkUl6wRBUklWOPyfPRbeJbhvdhQYXEjVtZ1LI9amVMkIWQI2nO8O7DL9A==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.6.3.tgz", + "integrity": "sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", + "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.23.tgz", + "integrity": "sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "output-file-sync": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", + "integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "is-plain-obj": "^1.1.0", + "mkdirp": "^0.5.1" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-format": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", + "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prompts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", + "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "dev": true, + "requires": { + "kleur": "^3.0.2", + "sisteransi": "^1.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "psl": { + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "randomatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz", + "integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==", + "dev": true, + "requires": { + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp-tree": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz", + "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", + "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.0.2", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", + "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", + "dev": true + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "dev": true, + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + } + } + }, + "resolve": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "rollup": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.15.1.tgz", + "integrity": "sha512-JErZxFKs0w7wpHZXWonAlom1Jezo0gJ7mf7JHTjOAjFGKAqNMEnlzEjMYhy6cqHgSfSPj/idVscuW+Lo6y6AoQ==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "^12.0.7", + "acorn": "^6.1.1" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + } + } + }, + "rollup-plugin-commonjs": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.0.0.tgz", + "integrity": "sha512-B8MoX5GRpj3kW4+YaFO/di2JsZkBxNjVmZ9LWjUoTAjq8N9wc7HObMXPsrvolVV9JXVtYSscflXM14A19dXPNQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.0", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.10.1", + "rollup-pluginutils": "^2.7.0" + } + }, + "rollup-plugin-local-resolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/rollup-plugin-local-resolve/-/rollup-plugin-local-resolve-1.0.7.tgz", + "integrity": "sha1-xIZwFxbBWt0hJ1ZcLqoQESMyCIc=", + "dev": true + }, + "rollup-plugin-node-resolve": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.0.1.tgz", + "integrity": "sha512-9s3dTu44SKQZM/Pwll42GpqXgT+WdvO0Ga01lF8cwZqJGqRUATtD+GrP3uIzZdpnbPonEJbVasfFt80VGPQqKw==", + "dev": true, + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.0" + }, + "dependencies": { + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + } + } + }, + "rollup-pluginutils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz", + "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "shortid": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.13.tgz", + "integrity": "sha512-dBuNnQGKrJNfjunmXI2X7bl1gnMO4PwbNxrTzO1JvilODmL7WyyCtA+DYxe9XunLXmxmgzFIvKPQ6XRAQrr46Q==", + "requires": { + "nanoid": "^1.0.7" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sisteransi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", + "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", + "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", + "dev": true + }, + "spdx-correct": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", + "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.matchall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", + "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "regexp.prototype.flags": "^1.2.0" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, + "table": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "dev": true, + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "test-exclude": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.3.tgz", + "integrity": "sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "micromatch": "^2.3.11", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000000..43831ab082 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,83 @@ +{ + "name": "budibase-core", + "version": "0.0.1", + "description": "core javascript library for budibase", + "main": "/dist/budibase-core.umd.js", + "module": "dist/budibase-core.esm.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "jest", + "build": "rollup -c rollup.config.js" + }, + "keywords": [ + "budibase" + ], + "author": "Michael Shanks", + "license": "MPL-2.0", + "jest": { + "globals": { + "GLOBALS": { + "client": "web" + } + }, + "testURL": "http://jest-breaks-if-this-does-not-exist", + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/internals/mocks/fileMock.js", + "\\.(css|less|sass|scss)$": "identity-obj-proxy" + }, + "moduleFileExtensions": [ + "js" + ], + "moduleDirectories": [ + "node_modules", + "app/node_modules" + ], + "transform": { + "^.+\\.js$": "babel-jest" + }, + "transformIgnorePatterns": [ + "/node_modules/(?!svelte).+\\.js$" + ] + }, + "devDependencies": { + "@babel/cli": "^7.4.4", + "@babel/core": "^7.4.5", + "@babel/plugin-transform-runtime": "^7.4.4", + "@babel/preset-env": "^7.4.5", + "@babel/runtime": "^7.4.5", + "argon2": "^0.20.1", + "babel-eslint": "^10.0.2", + "babel-jest": "^23.6.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", + "cross-env": "^5.1.4", + "eslint": "^5.3.0", + "eslint-config-airbnb": "^17.1.0", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-jsx-a11y": "^6.1.1", + "eslint-plugin-react": "^7.11.0", + "jest": "^24.8.0", + "readable-stream": "^3.1.1", + "regenerator-runtime": "^0.11.1", + "rimraf": "^2.6.2", + "rollup": "^1.12.0", + "rollup-plugin-commonjs": "^10.0.0", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-node-globals": "^1.4.0", + "rollup-plugin-node-resolve": "^5.0.0" + }, + "dependencies": { + "@nx-js/compiler-util": "^2.0.0", + "date-fns": "^1.29.0", + "lodash": "^4.17.11", + "lunr": "^2.3.5", + "safe-buffer": "^5.1.2", + "shortid": "^2.2.8" + }, + "devEngines": { + "node": ">=7.x", + "npm": ">=4.x", + "yarn": ">=0.21.3" + } +} diff --git a/packages/core/q b/packages/core/q new file mode 100644 index 0000000000..cdf63d511e --- /dev/null +++ b/packages/core/q @@ -0,0 +1,1131 @@ +commit d5a047a435cd1c1a987f02a0a6e02e4682d6fb93 (HEAD -> master) +Author: danbudi +Date: Mon May 6 23:26:38 2019 +0100 + + retry logic tests + +commit 1211d73c8e9245c644c5ecbb860d9aeccd58dfd4 +Merge: af625aa 6fa8502 +Author: danbudi +Date: Sun May 5 23:22:11 2019 +0100 + + merge upstream + +commit af625aa0d0955c7ff39975f76ae93b022ba919ed +Author: danbudi +Date: Sat May 4 12:02:21 2019 +0100 + + retry logic in common/index.js + +commit 6fa85022a7aeff9b501b6f7ce4d0d95bd16dbedf +Author: danbudi <50263791+danbudi@users.noreply.github.com> +Date: Sat May 4 12:00:37 2019 +0100 + + Update AUTHORS.md + +commit 4b3dbf25affa8fd08b8ad3f363b3eceb22c14390 (origin/master, origin/HEAD) +Author: Mike Shanks <49766997+mikebudi@users.noreply.github.com> +Date: Wed Apr 24 20:04:24 2019 +0100 + + typo in readme + +commit ece328a481ef4cd0c1d6950b87d7b5d54bc276fa +Author: michael shanks +Date: Wed Apr 24 16:39:03 2019 +0100 + + added contributing guidelines + +commit 09e35e2971fb7d3601813eeb5a001d4ad528a4da +Author: michael shanks +Date: Mon Apr 22 22:53:06 2019 +0100 + + updated readme + +commit ade153d2e9058e146fe670c0f004b909105c9d30 +Merge: e2e6646 4ce30fa +Author: Michael Shanks +Date: Sun Apr 14 08:01:13 2019 +0100 + + Merge branch 'master' of gitlab.com:budibase-source/budibase-core into removing_collections + +commit e2e6646670c213fff7b74f66ac09a0937245719f +Author: Michael Shanks +Date: Sun Apr 14 08:00:57 2019 +0100 + + no collections - all passing + +commit da9d907e535111a50056664f143d0ba136ec2264 +Author: Michael Shanks +Date: Fri Apr 12 18:28:40 2019 +0100 + + removing collections .. close + +commit 4ce30fabc30e376084e9020cd1e86a6216b53867 +Author: Michael Shanks +Date: Fri Apr 12 12:52:51 2019 +0000 + + Update readme.md + +commit 74c4580ea1f42730ff34cf1b2b4cd9d0f6ac012d +Author: = +Date: Fri Apr 12 09:15:28 2019 +0100 + + exporting all hierarchy methods + +commit 5b13e86e30d482b250c15e7d0444be5686a6f351 +Author: = +Date: Fri Apr 12 09:08:45 2019 +0100 + + exporting hierarchy ethods + +commit 7bbf6ef76d41cbca43e0987c011c860cf869e027 +Merge: d9d8d1f 215947c +Author: = +Date: Wed Apr 10 22:37:41 2019 +0100 + + Merge branch 'master' of gitlab.com:budibase-source/budibase-core + +commit d9d8d1f58f7d64dce572d609dd69e75de0834253 +Author: = +Date: Wed Apr 10 22:36:18 2019 +0100 + + exposed constructHierarchy + +commit 215947cb454fc01042c83ffe05d266d18fde99ab +Author: Michael Shanks +Date: Mon Apr 8 21:24:06 2019 +0000 + + Update package.json - MPL license + +commit f2fca48b270537f44db3654a6167f9475fef09b6 +Author: Michael Shanks +Date: Mon Apr 8 21:04:13 2019 +0000 + + Update readme.md - some basic getting started + +commit 0668df1f949f1773c57d23e4e0d9afc2a0cbcbdb +Author: Michael Shanks +Date: Fri Apr 5 16:31:02 2019 +0100 + + backup... + +commit 47e2346a1e6ece50bb91295d1c0358733648be81 +Author: Michael Shanks +Date: Wed Mar 27 22:21:11 2019 +0000 + + typo.. + +commit 5de74d1c242fb0e84a37805ff0fead28eb61a9f9 +Author: Michael Shanks +Date: Wed Mar 27 22:20:07 2019 +0000 + + getAppApis - accepts app definition as arg + +commit 1a390d51c35ef3c413969690a7813673214d8123 +Author: Michael Shanks +Date: Mon Mar 25 21:26:55 2019 +0000 + + BIG refactor - all indexes on records, and root, not collection + +commit b7deaef9482340daf6b0ccc71d387a72cc09c4b8 +Author: Michael Shanks +Date: Sat Mar 23 23:56:32 2019 +0000 + + upload/download files complete + +commit 548f2a2713eff415d6102b6de1bacef7f1510680 +Author: Michael Shanks +Date: Sat Mar 23 19:08:54 2019 +0000 + + fixed failing tests after added file field + +commit 90214c8931ba1648cd8c7493d816f9b4f080af0d +Author: Michael Shanks +Date: Fri Mar 22 22:05:54 2019 +0000 + + fixed some failing tests + +commit d60ddc65f47593c382e1d0b14d530fdf4bf353f1 +Author: Michael Shanks +Date: Fri Mar 22 21:49:04 2019 +0000 + + untested upload/download file + +commit 5160bca054ea9af4222e1d4fd91f9ff2152f7995 +Author: Michael Shanks +Date: Thu Mar 21 13:31:43 2019 +0000 + + database manager & more exports + +commit 9f85a24352dc1f506b4c500c8fba2dbc256823e3 +Author: Michael Shanks +Date: Thu Mar 21 10:03:31 2019 +0000 + + databaseFactory > databaseManager + +commit 3e2a62f6c0a46b4859f61f94279bebec6906c458 +Author: Michael Shanks +Date: Thu Mar 21 09:25:15 2019 +0000 + + renaming typo... + +commit 6cf600cdd1ce61a9cd07a4e1511d2152ff9f93df +Author: Michael Shanks +Date: Thu Mar 21 09:22:31 2019 +0000 + + bla + +commit ed18edfa21b06097a8328a2d57e4191067b384fd +Author: Michael Shanks +Date: Tue Mar 19 21:24:44 2019 +0000 + + removing createemptydb for normal circumstances + +commit c180da2b1c707179d5ddc9cad34c4c7599636fb3 +Author: Michael Shanks +Date: Tue Mar 19 21:21:00 2019 +0000 + + validation on create database + +commit cc1f0db735a0c789f0774946ebf809ed4e1302da +Author: Michael Shanks +Date: Tue Mar 19 21:06:58 2019 +0000 + + database creator + +commit e43750345a65c41b0bea9171f8fbd79e2b0e184e +Author: Michael Shanks +Date: Tue Mar 19 20:48:17 2019 +0000 + + removing storeHandle from apis + +commit 7a58d2d1e86b935a30ae62aed42b7a0cd3de9873 +Author: Michael Shanks +Date: Sun Mar 17 22:35:02 2019 +0000 + + setUserAccessLevels + +commit a99e3fab43f5c887c1f0e34e2962ca352740d1d1 +Author: Michael Shanks +Date: Sun Mar 17 20:26:43 2019 +0000 + + added initialiseData to index.js + +commit 2eb827025db50c2cf2a174a2774b92a7ac3ec1d8 +Author: Michael Shanks +Date: Sun Mar 17 19:40:59 2019 +0000 + + initialise data tests passing + +commit ac0c29fc28c79445a9ab9c9e66cc81d5826ed85d +Author: Michael Shanks +Date: Wed Mar 13 22:40:48 2019 +0000 + + initialise app method + +commit 5b8ccc80e6d96b6f8bdc3533b3742ffb070de1af +Author: Michael Shanks +Date: Mon Mar 11 22:46:51 2019 +0000 + + actionsApi Auth - tested + +commit 2208f62b51eec9594dfe454c4248830824e20222 +Author: Michael Shanks +Date: Sun Mar 10 22:42:19 2019 +0000 + + renamed actions test file + +commit 870834516e6279524146227f5347f0d3892359fe +Author: Michael Shanks +Date: Sun Mar 10 22:41:17 2019 +0000 + + refactor to allow actions to be called as api + +commit ba00ef003273e1e569a8de30199a4f6986bbb0a6 +Author: Michael Shanks +Date: Sat Mar 9 22:18:52 2019 +0000 + + authApi isAuthorized testing + +commit f00a2a6ff15ca4f1520ad7b6741794581f22c7cd +Author: Michael Shanks +Date: Fri Mar 8 22:48:17 2019 +0000 + + auth tests on app hierarchy + +commit 63affd0056eddf775a157eefa414d6c378fc371e +Author: Michael Shanks +Date: Thu Mar 7 22:26:51 2019 +0000 + + more auth testing + +commit 83e382d36239fd7235e5021d679c871f9881fe70 +Author: Michael Shanks +Date: Thu Mar 7 21:38:17 2019 +0000 + + more auth testing + +commit a64c3fd97127da3438d70d0dc9ce518b553840f9 +Author: Michael Shanks +Date: Wed Mar 6 22:30:22 2019 +0000 + + fixed broken tests + +commit 36091652b05b8eb95f88c71defa4f80e517037ce +Author: Michael Shanks +Date: Wed Mar 6 21:50:01 2019 +0000 + + testing recordApi auth + +commit bec0f94a62ed62320a019c3a581bac934d4fe0c1 +Author: Michael Shanks +Date: Tue Mar 5 22:21:20 2019 +0000 + + isAuthorized integrated into api functions + +commit 7f5c830f4a90ecbe95bacf16fb74c0397a0cb02b +Author: Michael Shanks +Date: Mon Mar 4 19:42:25 2019 +0000 + + split apiWrapper for async and sync + +commit faf42685b79ac0ba07fff3375e61bfa7ab918f6d +Author: Michael Shanks +Date: Fri Mar 1 22:44:42 2019 +0000 + + plugin points for authApi ... failing.. + +commit 9735eea4a1acfc272fd3103fe70d54fcf5a2199b +Author: Michael Shanks +Date: Thu Feb 28 22:49:44 2019 +0000 + + enable disable user + +commit 4da747abece4f5d64b55a7ff72da8820b20be2b6 +Author: Michael Shanks +Date: Wed Feb 27 22:10:47 2019 +0000 + + setting & resetting passwords + +commit 2b75d12fb9ea3c8d0d31149c9b5799ae56b0fc6d +Author: Michael Shanks +Date: Mon Feb 25 23:34:14 2019 +0000 + + typo.. + +commit b5f8e6c2f72a5ca0b5c9c2b1ff8721cd4df6c2dc +Author: Michael Shanks +Date: Mon Feb 25 23:30:31 2019 +0000 + + authentication - temporar access + +commit 52eebdf96ddb033ca87b7b925341395a4e16c1d0 +Author: Michael Shanks +Date: Mon Feb 25 22:43:10 2019 +0000 + + authentication testing.. + +commit f7521cd0782e3990c5bbefeca0a27a16eeb173ba +Author: Michael Shanks +Date: Sun Feb 24 22:37:59 2019 +0000 + + started authenticate + +commit 636cfd45144e410c84242670797ce93c014fb38b +Author: Michael Shanks +Date: Sat Feb 23 22:28:45 2019 +0000 + + create user testing + +commit a61ae11994349ff2837f62578234ca5483732919 +Author: Michael Shanks +Date: Sat Feb 23 20:41:29 2019 +0000 + + validateUses tests + +commit 31361c918298b1642165ee0cba211c055ea73073 +Author: Michael Shanks +Date: Sat Feb 23 10:24:59 2019 +0000 + + createUser testing + +commit 1ec8e0df2a8da2e8e4d8ec5fe121552e8d1f329a +Author: Michael Shanks +Date: Fri Feb 22 21:23:05 2019 +0000 + + save access levels... test complete + +commit aadf821a77a34428ade4c46b36fcf82084b6cbac +Author: Michael Shanks +Date: Fri Feb 22 12:30:09 2019 +0000 + + few pssing on validation of access levels + +commit b5246be7fce6858bf24a475783ddb01ba7c4dc89 +Author: Michael Shanks +Date: Fri Feb 22 11:21:37 2019 +0000 + + access level validateion + +commit 22bc3fc4a208aa8a452af5b92542fb4d11e46632 +Author: Michael Shanks +Date: Thu Feb 21 23:43:22 2019 +0000 + + access levels - moved validationCommon + +commit ab5a24ba0c09a1784c18144f85532452124ea818 +Author: Michael Shanks +Date: Wed Feb 20 22:54:11 2019 +0000 + + access levels... + +commit 4795f33926da83e7de209d44f42688bb84b7f4c2 +Author: Michael Shanks +Date: Tue Feb 19 22:49:18 2019 +0000 + + starting access levels + +commit 1f818669eab153e856477ff838269e80db4d662e +Author: Michael Shanks +Date: Tue Feb 19 22:05:00 2019 +0000 + + authApi - wrapper + +commit 3861a4785f9acd5565904d1553d71c2f0163d49a +Author: Michael Shanks +Date: Tue Feb 19 21:17:02 2019 +0000 + + fixing broken test + +commit ce96870c2ca71af99920075b75aaf95316c81e10 +Author: Michael Shanks +Date: Mon Feb 18 22:48:31 2019 +0000 + + username case insensitive + +commit 78c921be09cd816e0e8be4df8e3ea057f65c71f9 +Author: Michael Shanks +Date: Mon Feb 18 22:43:03 2019 +0000 + + make sure returned users contain no hashes + +commit 8245baff9dd8ae994298a68b5322c515593b2898 +Author: Michael Shanks +Date: Mon Feb 18 22:36:40 2019 +0000 + + more auth + +commit bfd0085396158a083d095ac76f4722033d94abb7 +Author: Michael Shanks +Date: Mon Feb 18 22:08:29 2019 +0000 + + work on auth + +commit 9fdecbdea7145a5f2e9a0406b19be099e2176044 +Author: Michael Shanks +Date: Sun Feb 17 22:49:33 2019 +0000 + + user access file structure + +commit d73f01a48eb912241693a8cd2be48bb5918920e0 +Author: Michael Shanks +Date: Thu Feb 14 20:16:05 2019 +0000 + + removed collectionApi.listRecords + +commit 8603817456d52e75011d8c0abd0ad7830b744565 +Author: Michael Shanks +Date: Thu Feb 14 20:10:29 2019 +0000 + + concurrency tests + +commit 002b9a89a03bc8c065bd0bafefe0d9d9c73fb34d +Author: Michael Shanks +Date: Wed Feb 13 22:35:30 2019 +0000 + + added a load of concurrency tests + +commit 69a086a8aa0bcf61af020705406e78e77e9f66d1 +Author: Michael Shanks +Date: Tue Feb 12 22:01:13 2019 +0000 + + all tests passing ! + +commit 95c36598463014f14bc503eaf482eff648ad3d9b +Author: Michael Shanks +Date: Mon Feb 11 23:31:23 2019 +0000 + + tidy up + +commit c9d1c7454fc343414bd8e8b9bc28e2afa39891e4 +Author: Michael Shanks +Date: Mon Feb 11 23:30:13 2019 +0000 + + reverse to go only + +commit c1707f232f414254fb19ffe6e4c5d509e2977a52 +Author: Michael Shanks +Date: Mon Feb 11 22:40:21 2019 +0000 + + getting better.. + +commit 6815f18351ce4d97d6e6b7e70997194567f26029 +Author: Michael Shanks +Date: Mon Feb 11 21:38:53 2019 +0000 + + commiting before i break it all + +commit 52425588910bdd802ffb98ddbd2b7f570b81a6c0 +Author: Michael Shanks +Date: Sun Feb 10 22:45:52 2019 +0000 + + buildindex tests... + +commit c1df98f5d164c2b719a1e62788321dcbedaa28fc +Author: Michael Shanks +Date: Sun Feb 10 00:01:23 2019 +0000 + + buildindex some tests starting to pass + +commit 8506d019e5e27160924e6e4505ae0c78e658b05f +Author: Michael Shanks +Date: Fri Feb 8 22:13:36 2019 +0000 + + build index - tests failing + +commit ff33499debdd8ccc271e35251b88e7bd4b59ec5d +Author: Michael Shanks +Date: Wed Feb 6 20:57:50 2019 +0000 + + just buildIndex to go... + +commit bb1c54a168c52d4ff49515d85fecf80d4885cf67 +Author: Michael Shanks +Date: Tue Feb 5 22:46:35 2019 +0000 + + working down the errors + +commit 613c93d4a60e101c1801ed1ddc54094c0a4650cd +Author: Michael Shanks +Date: Mon Feb 4 23:05:42 2019 +0000 + + working thru errors... + +commit eb6971c4f9d04c9e0c6d696520960fdcc97418b8 +Author: Michael Shanks +Date: Sun Feb 3 22:25:57 2019 +0000 + + working through errors + +commit b6bb57f501a10f7d60bb35fcfa4653278ddab808 +Author: Michael Shanks +Date: Sun Feb 3 00:13:45 2019 +0000 + + everything broken , fear not + +commit c50fd93a1fb9b7d14bb53634208fbd31b4be5815 +Author: Michael Shanks +Date: Sat Feb 2 23:17:28 2019 +0000 + + massive changes, everything rekt + +commit e036e0a2867eb50868ecf687c09fcc8cb373372e +Author: Michael Shanks +Date: Sat Feb 2 22:12:14 2019 +0000 + + bacuk before it all goes wrong + +commit 226025550b7ffbaf15bda07f35be355b5bd19239 +Author: Michael Shanks +Date: Thu Jan 31 21:50:34 2019 +0000 + + dodgy backup + +commit 90b4aec2e24c9953d3beb19d4420c00f685ea952 +Author: Michael Shanks +Date: Wed Jan 30 22:41:14 2019 +0000 + + starting transactions + +commit 40819bee5c720e70a7a010b27d41ac6b5fbc555c +Author: Michael Shanks +Date: Tue Jan 29 19:53:02 2019 +0000 + + load, loads references + +commit b626cf058fdec3f5d8a90cb404b29654f3617711 +Author: Michael Shanks +Date: Mon Jan 21 22:27:49 2019 +0000 + + some packaging stuff + +commit 24e02b18207aff073345f86db4cc7c522606a938 +Author: Michael Shanks +Date: Mon Jan 21 22:21:37 2019 +0000 + + package updates + +commit d83b66eb8aba3225707e658c94b1e2256cef2fc5 +Author: Michael Shanks +Date: Mon Jan 21 22:19:02 2019 +0000 + + referential integrity + +commit 24f884b378f3950bd01b1abe6eb9b788159258f8 +Author: Michael Shanks +Date: Mon Jan 21 19:42:12 2019 +0000 + + finish refactoring reverseIndexNodeKey on field, to be array + +commit 2f16c86ba7db5675001875cd3fdadcfd776dfa97 +Author: Michael Shanks +Date: Sun Jan 20 22:23:38 2019 +0000 + + started making reverseIndexNode key to array + +commit 20b076dc636a5cc8c8e555f4945165a29b39b04c +Author: Michael Shanks +Date: Sun Jan 20 22:04:41 2019 +0000 + + added some safety around deleting indexes + +commit ffccb15b849a8186f7d5bd2f0faa40e0b81efe9d +Author: Michael Shanks +Date: Sun Jan 20 01:37:06 2019 +0000 + + typeContraint - fixing tests + +commit 83206f6404b794e16b41c3c631f5b3fd3dcb508d +Author: Michael Shanks +Date: Sun Jan 20 01:14:36 2019 +0000 + + tests for typeOptions validations + +commit 77ce948d54290aa5e95ed88546f69e742eabcdb7 +Author: Michael Shanks +Date: Sun Jan 20 00:22:58 2019 +0000 + + big refactor of type contraints + +commit c1445cef4c6456ebaf1f514791e08856ceef5789 +Author: Michael Shanks +Date: Sat Jan 19 20:29:36 2019 +0000 + + investigation: + skipped test : failing test on nested references + +commit 1bba64b7b9f083700f8be75d3e9c012caed69be0 +Author: Michael Shanks +Date: Fri Jan 18 21:57:23 2019 +0000 + + refactoring to allow references do validate that value belongs to index + +commit d7c4614ed84cc39a4a88b361669eba232b25dc54 +Author: Michael Shanks +Date: Wed Jan 16 21:35:49 2019 +0000 + + failing test for updated reference + +commit 85a4b41847b7cb1a5f526d5ee8bb7397a4f9c07b +Author: Michael Shanks +Date: Wed Jan 16 20:52:13 2019 +0000 + + tests for search + +commit 513551b8b0142a14142bab0cb23770488ab876fd +Author: Michael Shanks +Date: Tue Jan 15 22:46:13 2019 +0000 + + implemented search - no tests + +commit 241105f71e1e17761de8ccd6e58fefd351bdf267 +Author: Michael Shanks +Date: Tue Jan 15 22:28:56 2019 +0000 + + refactoring to allow search phrase in list items + +commit 2208be1a64c7299cedfb59e125bda0a64c4a5b1d +Author: Michael Shanks +Date: Mon Jan 14 22:12:46 2019 +0000 + + aggregates - -done + +commit f5d4d4cfe1a66c9b9d490e22958faa07c59f0143 +Author: Michael Shanks +Date: Sun Jan 13 22:00:15 2019 +0000 + + aggregates... + +commit 1236f264bd7442f26268a0ee7af5968e4a0040e9 +Author: Michael Shanks +Date: Fri Jan 11 21:49:58 2019 +0000 + + all tests passing tf + +commit 929fff39c79efbebd9bbc5a65b4872e5f662d04c +Author: Michael Shanks +Date: Wed Jan 9 22:22:32 2019 +0000 + + more tests passing yay + +commit 0e1f32d8b0b062ae454f8ce93246a09ced164ca3 +Author: Michael Shanks +Date: Sun Jan 6 00:06:18 2019 +0000 + + working through errors.... + +commit a1b2b3430a2e130832d9a4ee1a44a1a0d1c768e9 +Author: Michael Shanks +Date: Sat Jan 5 22:34:54 2019 +0000 + + schema generator works + +commit f17dd45a7377780b03abf2ff289813e2610023ef +Author: Michael Shanks +Date: Fri Jan 4 23:00:41 2019 +0000 + + starting testing of index schema generator + +commit 44aecb8c5caf966a2c38149f6fdec90f5aaa491f +Author: Michael Shanks +Date: Fri Jan 4 21:44:51 2019 +0000 + + half way through index streaming + +commit 4503b890cea6d88a5790b5bd23577c58e945cf21 +Author: Michael Shanks +Date: Mon Dec 31 08:52:27 2018 +0000 + + index only relevant if allowedRecordNodeId contains records nodeid + +commit 3d53721cc7dbd6d07f126cbbb920b4cc466d4e95 +Author: Michael Shanks +Date: Sat Dec 29 00:22:36 2018 +0000 + + added recordNodeId as global id, instead of collectionChildId + +commit 3a85968dce9b71ca68adfeaf93b925bc7715b033 +Author: Michael Shanks +Date: Fri Dec 28 22:17:14 2018 +0000 + + all tests passing + +commit 284ebf1ed7f396691ba9268a74a5f62868a790e2 +Author: Michael Shanks +Date: Fri Dec 28 22:11:13 2018 +0000 + + aggregates - abandoning for now... + +commit 14ea83f029a4c1a9b9fd983331277271986a19e8 +Author: Michael Shanks +Date: Tue Dec 18 22:05:41 2018 +0000 + + bugfix.. reloading of hierarchy not rehydrating aggregates + +commit f7053af9e503e86a12fc873b04fdc58c6573343e +Author: Michael Shanks +Date: Tue Dec 18 21:39:23 2018 +0000 + + aggregates - templateApi validation + +commit c3af14e18fb02fc92d6dcbde95a67b8a2a6793c2 +Author: Michael Shanks +Date: Mon Dec 17 22:15:14 2018 +0000 + + getting ready to start dev on aggregates + +commit 8ee9205cd9a825642c397cc1cd22cd42f473e539 +Author: Michael Shanks +Date: Tue Dec 11 21:04:32 2018 +0000 + + index sharding complete + +commit e1cda0d24ac1da947646535cd1c4d2841837f23f +Author: Michael Shanks +Date: Mon Dec 10 22:26:44 2018 +0000 + + more tests for sharded indexes + +commit 5cbe367461f1ca060ea9b6b0183ed52a3568977c +Author: Michael Shanks +Date: Mon Dec 10 21:18:06 2018 +0000 + + refactoring build index for sharding... old tests passing + +commit b2555c52c0e3c9ebb0087e190ff35987709de6e6 +Author: Michael Shanks +Date: Sat Dec 8 21:39:00 2018 +0000 + + list items - testing sharded indexs + +commit 7a549c87ef7133407bfe92afe4721f12349034ea +Author: Michael Shanks +Date: Sat Dec 8 14:05:30 2018 +0000 + + index shrading tests passing again + +commit c6d067f248d3faefa0791e95725cebb4ec0060e6 +Author: Michael Shanks +Date: Fri Dec 7 23:27:03 2018 +0000 + + index shrading WIP + +commit 670e9941cc8901d9764436fd70094eb6e904f0fa +Author: Michael Shanks +Date: Thu Dec 6 23:02:18 2018 +0000 + + index sharding refactoring + +commit 969c4801c107eb3958d1ef9d9c559ac03d7da7b2 +Author: Michael Shanks +Date: Wed Dec 5 22:28:02 2018 +0000 + + refactoring for shard index... + +commit 5df82b19fa6954be20b9d3a1cf0a5f93b06cc9e6 +Author: Michael Shanks +Date: Mon Dec 3 21:40:16 2018 +0000 + + remove from allids - now used and tested + +commit a04500193b227ede3bd48eee3193dd6760a82476 +Author: Michael Shanks +Date: Thu Nov 29 09:20:10 2018 +0000 + + fields validated on hierarchy validation + +commit 603382aa3067d1b429ea5761bd005dd3a69cdb59 +Author: Michael Shanks +Date: Tue Nov 27 22:14:16 2018 +0000 + + lots more testing of buildIndex for reverse indexing + +commit afc29d2ecc7b8ab661afd116ced9e8ff69031142 +Author: Michael Shanks +Date: Sun Nov 25 22:49:39 2018 +0000 + + reverse index - rebuild, just starting... + +commit ddd70e9aa8a91f3d8713d3d3a69f27720869a4d8 +Author: Michael Shanks +Date: Sun Nov 25 21:22:42 2018 +0000 + + reverse indexes tested yay + +commit 044e955658695ba875d151252bbab471b8b1233a +Author: Michael Shanks +Date: Sun Nov 25 20:34:27 2018 +0000 + + more testing revers indexes + +commit 1dbdb9fa26f38ca938bc269988f55f28cfd94d1b +Author: Michael Shanks +Date: Fri Nov 23 22:49:51 2018 +0000 + + reverse index failing test - reindex for update + +commit 5ff917a748a8db764fa4a4e451f942d183f5d5bc +Author: Michael Shanks +Date: Fri Nov 23 21:26:21 2018 +0000 + + reverse indexes all passing... amybe need more tests + +commit a507978efaadb527b24b6e7747a972e11c8f7765 +Author: Michael Shanks +Date: Thu Nov 22 22:40:38 2018 +0000 + + reverse indexes getIndexedDataKey using mappedRecord for path + +commit d0017223799947ce027b5917b042fe3b1b155e1a +Author: Michael Shanks +Date: Wed Nov 21 22:51:24 2018 +0000 + + reverse index - reindex - failing test + +commit d6fbd485e0c6a17e80dfce8c1ef15c6e6d2796e8 +Author: Michael Shanks +Date: Wed Nov 21 21:48:55 2018 +0000 + + reference indexes testing.. + +commit eca8bb8b87e762b906c1e8c7828aa523ed0eb238 +Author: Michael Shanks +Date: Tue Nov 20 22:40:14 2018 +0000 + + reverse reference indexes - shaky + +commit 0e79d4e2fba773d35eb76e0603fc770172347798 +Author: Michael Shanks +Date: Mon Nov 19 21:36:23 2018 +0000 + + removed view concept + +commit b1825f1d3c9495dc401c513f171c91660bb5d9c6 +Author: Michael Shanks +Date: Sun Nov 18 22:16:45 2018 +0000 + + index validation, no tests + +commit 7fbba8dfa4575a3ddae50963d8f17544aa40d003 +Author: Michael Shanks +Date: Sat Nov 17 22:57:47 2018 +0000 + + validation common module + +commit 4edb6911a2330e17ddbdbdeb9aefdc277489ec40 +Author: Michael Shanks +Date: Fri Nov 16 22:39:40 2018 +0000 + + heararchy validation + +commit ad464d8a5a2a3dae3820dc9df4e9bc2701d1d11d +Author: Michael Shanks +Date: Fri Nov 16 19:00:56 2018 +0000 + + calling validate on save hierarchy + +commit 4015a29e7f9f69c26fb87af328aaa2d1dfee9008 +Author: Michael Shanks +Date: Thu Nov 15 22:14:03 2018 +0000 + + triggers! + +commit aa2a1f9c2b0f868ddaf378de9a62a66707ea430a +Author: Michael Shanks +Date: Wed Nov 14 20:42:47 2018 +0000 + + action execution + +commit 422546979af609274996dcc8a9ced221cc7e4438 +Author: Michael Shanks +Date: Tue Nov 13 22:36:56 2018 +0000 + + still failing - getting into debuggine of initialse actions + +commit aca46047dc6c5febd948fdc286c40389aeb1ec17 +Author: Michael Shanks +Date: Tue Nov 13 22:26:53 2018 +0000 + + actions initialise - failing tests + +commit 3eed51db82fad61a377a173cd3f2695aa36aa9d2 +Author: Michael Shanks +Date: Mon Nov 12 21:41:28 2018 +0000 + + actions and triggers creation + +commit 66672605846692d87645f49ab646b51bc05c3e9a +Author: Michael Shanks +Date: Sun Nov 11 23:25:32 2018 +0000 + + few more tests for action and trigger validation + +commit efd9648ae6a00c7e197f0a241a704c112379d24b +Author: Michael Shanks +Date: Sun Nov 11 23:23:27 2018 +0000 + + action and trigger validation + +commit 0a7374ee6c382aaa497d9e42b209e0aa1f3e90b2 +Author: Michael Shanks +Date: Fri Nov 9 22:54:19 2018 +0000 + + action validation starter + +commit 9528e4ea670e6642105a0a38903401895d86eac5 +Author: Michael Shanks +Date: Fri Nov 9 21:30:30 2018 +0000 + + actions started + +commit 6f16dbeba7d4e2b21f9874ee5a3baaa6f542e0f6 +Merge: 3db75d0 d1d9c86 +Author: Michael Shanks +Date: Fri Nov 9 18:44:53 2018 +0000 + + Merge branch 'master' of gitlab.com:budibase-source/budibase-core + +commit 3db75d02025f95f8011972fd37c674b909b7fea0 +Author: Michael Shanks +Date: Fri Nov 9 18:44:40 2018 +0000 + + appHierarchy > appDefinition (inc. actions) + +commit d1d9c869cc99a9da46dbbdf1efa2233743297ea6 +Author: Michael Shanks +Date: Thu Nov 8 16:33:50 2018 +0000 + + Add LICENSE + +commit f960ff58ee147a4c220b4a474224746e31b8aa56 +Author: Michael Shanks +Date: Wed Nov 7 21:57:05 2018 +0000 + + delete record working + +commit ff1f956d84375a1489e2fe593c52e9b1deec466b +Author: Michael Shanks +Date: Tue Nov 6 21:55:41 2018 +0000 + + delete working 99% - race condition somewhere + +commit 72d35f5da5254cbcea74e34da30948428059c15b +Author: Michael Shanks +Date: Sun Nov 4 22:20:32 2018 +0000 + + allids folders created + +commit 35ba20f11d1c99c8485389bcb8124de547f69681 +Author: Michael Shanks +Date: Sat Nov 3 09:29:27 2018 +0000 + + records now saved at /colection/1234/record.json + +commit 8098d2764b122c4837bc56a469ab83a358b87004 +Author: Michael Shanks +Date: Sat Nov 3 09:07:04 2018 +0000 + + creating .config folder + +commit 595ac9da001eab24ede2f019a3863d4dd91e8fde +Author: Michael Shanks +Date: Sat Nov 3 08:52:41 2018 +0000 + + adding folder for collection initialise + +commit f0974a618e1d9b67dadc9853be22fb8084b286bd +Author: Michael Shanks +Date: Tue Oct 30 21:49:28 2018 +0000 + + exporting setupDatastore + +commit 022927ad188686df79511c4d9ce9a42be1906901 +Author: Michael Shanks +Date: Tue Oct 30 21:36:46 2018 +0000 + + events export with main module + +commit 2a64c73ec80d63c0cdac72fa7ca438b6cfd570f3 +Author: Michael Shanks +Date: Tue Oct 30 20:52:42 2018 +0000 + + events list + +commit 641f3c7cddb4dcee7516efdc9efcd8c95dbe0fb3 +Author: Michael Shanks +Date: Tue Oct 30 20:49:46 2018 +0000 + + flat event list + +commit d68025cab9d1ab650b380fd06cbbec6e0610ae78 +Author: Michael Shanks +Date: Tue Oct 30 19:03:16 2018 +0000 + + test passing... + +commit 716c889aa33f8303c18eed67619b2766eb921f66 +Author: Michael Shanks +Date: Mon Oct 29 23:16:09 2018 +0000 + + added elapsed & threadIds + +commit aa8da8fe20cc4e6229a34f6a8cff7e4fdb0fda74 +Author: Michael Shanks +Date: Sun Oct 28 00:25:58 2018 +0100 + + unused inports + +commit 852843826742ac542f4cdcac2ebb252348c7793a +Author: Michael Shanks +Date: Sun Oct 28 00:23:39 2018 +0100 + + all events in one file + +commit dfe9c51906cdc66909eb589f52e103dafb655b8c +Author: Michael Shanks +Date: Sat Oct 27 22:37:40 2018 +0100 + + events added to viewApi, collectionApi + +commit 75a59c29a2697ffb558d83af13d6a3687b1dcc9a +Author: Michael Shanks +Date: Sat Oct 27 21:56:04 2018 +0100 + + added events to all recordApi + +commit 19b062a6144a817142b420beba034c62ca4064ce +Author: Michael Shanks +Date: Fri Oct 26 23:20:25 2018 +0100 + + api wrapper tested + +commit 71c497397502d4ef50e21c22d8a931a388a87e0a +Author: Michael Shanks +Date: Fri Oct 26 21:23:57 2018 +0100 + + adding events - api wrapper + +commit d4a48494fbcf44f33bcca692f0f48718aa634acc +Author: Michael Shanks +Date: Wed Oct 24 22:36:00 2018 +0100 + + adding save events.... + +commit f8cbd187fc124bb13ffd314c649b9200a1d2df50 +Author: Michael Shanks +Date: Mon Oct 22 22:36:25 2018 +0100 + + added event aggregator + +commit f5a0df320659f41c1bfa86e7c7d8b7d30f439b5b +Author: Michael Shanks +Date: Mon Oct 22 21:43:02 2018 +0100 + + appHierarchy myst exist to initialise application + +commit a1a5d12db1eeb924f33d16eaac6dc4d04d476c34 +Author: Michael Shanks +Date: Sun Oct 21 21:54:53 2018 +0100 + + throwing error when app hierarchy does not exists - and everything breaks + +commit 5c081caf28f197b1445035df9e752a644809a122 +Author: Michael Shanks +Date: Sat Oct 20 23:28:50 2018 +0100 + + removing datastores - new repo + +commit 7c82b6ee305b4f2a50ecb0b2521d69e35d6281fe +Author: Michael Shanks +Date: Fri Oct 19 21:41:57 2018 +0100 + + rollup confing & package entry points + +commit bf563ff2126b951d4c14fa2bdaea07460b97c67e +Author: Michael Shanks +Date: Fri Oct 19 08:40:00 2018 +0100 + + added rollup - package to esm and cjs + +commit a906a3fa6ee0725c363f234083bbdcee5c7ca25f +Author: Michael Shanks +Date: Thu Oct 18 22:25:56 2018 +0100 + + removed unused ensureDefaults + +commit 6e0af0b0989bb58cad23e265026442b6b89d0eb5 +Author: Michael Shanks +Date: Thu Oct 18 22:23:35 2018 +0100 + + removed group concept + +commit ad19a896c1af11db1b2b9d6891a6d69c821c3321 +Author: Michael Shanks +Date: Thu Oct 18 21:36:06 2018 +0100 + + removing UI from hierarchy + +commit a97d65d7b1d802322fc263e49fd8440e1ef4d4df +Author: Michael Shanks +Date: Thu Oct 18 13:55:43 2018 +0100 + + initial commit - split core library diff --git a/packages/core/readme.md b/packages/core/readme.md new file mode 100644 index 0000000000..a08dc2b4f6 --- /dev/null +++ b/packages/core/readme.md @@ -0,0 +1,32 @@ +## Getting Started + +Install requires [node-gyp](https://github.com/nodejs/node-gyp), due to a dependancy on [argon2](https://github.com/ranisalt/node-argon2) + +### For node gyp on windows + +`npm install --global --production windows-build-tools` +and this might help: https://github.com/nodejs/node-gyp/issues/1278 + +### For node gyp on ubuntu + +`sudo apt-get install build-essentials` + +Once you have this, try... + +`npm install` + +Next, run the tests. Install jest, globally: + +`npm install -g jest` + +And finally, run + +`jest` + +## Documentation + +A work in progress, lives here: https://github.com/Budibase/docs/blob/master/budibase-core.md + + + + diff --git a/packages/core/rollup.config.js b/packages/core/rollup.config.js new file mode 100644 index 0000000000..824a5e65fb --- /dev/null +++ b/packages/core/rollup.config.js @@ -0,0 +1,76 @@ +import builtins from 'rollup-plugin-node-builtins'; +import resolve from 'rollup-plugin-node-resolve'; +import commonjs from 'rollup-plugin-commonjs'; +import nodeglobals from 'rollup-plugin-node-globals'; + +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"]; + +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 globals = { + "lodash/fp": "fp", + lodash: "_", + lunr: "lunr", + "safe-buffer": "safe_buffer", + shortid:"shortid", + "@nx-js/compiler-util":"compiler_util" +} + +module.exports = { + input: 'src/index.js', + output: [ + { + file: 'dist/budibase-core.cjs.js', + format: 'cjs', + sourcemap: 'inline', + globals + }, + /*{ + file: 'dist/budibase-core.iife.js', + format: 'iife', + sourcemap: 'inline', + globals: [ + + ] + }*/, + { + file: 'dist/budibase-core.esm.mjs', + format: 'esm', + sourcemap: 'inline', + globals + }, + { + file: 'dist/budibase-core.umd.js', + format: 'umd', + name: "budibase-core", + sourcemap: 'inline', + globals + } + ], + plugins: [ + nodeglobals(), + builtins(), + resolve({ + preferBuiltins:true + }), + commonjs({ + namedExports: { + "lodash/fp": lodash_fp_exports, + "lodash":lodash_exports, + "shortid": ["generate"] + } + }) + ], + external: [ + "lodash", "lodash/fp", "date-fns", + "lunr", "safe-buffer", "shortid", + "@nx-js/compiler-util" + ] + }; + + \ No newline at end of file diff --git a/packages/core/src/actionsApi/buildBehaviourSource.js b/packages/core/src/actionsApi/buildBehaviourSource.js new file mode 100644 index 0000000000..1d8f9784a1 --- /dev/null +++ b/packages/core/src/actionsApi/buildBehaviourSource.js @@ -0,0 +1,15 @@ +import { has } from 'lodash'; +import { ConflictError } from '../common/errors'; + +export const createBehaviourSources = () => { + const sources = {}; + const register = (name, funcsObj) => { + if (has(sources, name)) { + throw new ConflictError(`Source '${name}' already exists`); + } + + sources[name] = funcsObj; + }; + sources.register = register; + return sources; +}; diff --git a/packages/core/src/actionsApi/execute.js b/packages/core/src/actionsApi/execute.js new file mode 100644 index 0000000000..c5f44fc06d --- /dev/null +++ b/packages/core/src/actionsApi/execute.js @@ -0,0 +1,15 @@ +import { permission } from '../authApi/permissions'; +import { apiWrapperSync } from '../common/apiWrapper'; +import { events } from '../common/events'; + +export const executeAction = app => (actionName, options) => { + apiWrapperSync( + app, + events.actionsApi.execute, + permission.executeAction.isAuthorized(actionName), + { actionName, options }, + app.actions[actionName], options, + ); +}; + +export const _executeAction = (behaviourSources, action, options) => behaviourSources[action.behaviourSource][action.behaviourName](options); diff --git a/packages/core/src/actionsApi/index.js b/packages/core/src/actionsApi/index.js new file mode 100644 index 0000000000..c7e70bc213 --- /dev/null +++ b/packages/core/src/actionsApi/index.js @@ -0,0 +1,7 @@ +import { executeAction } from './execute'; + +export const getActionsApi = app => ({ + execute: executeAction(app), +}); + +export default getActionsApi; diff --git a/packages/core/src/actionsApi/initialise.js b/packages/core/src/actionsApi/initialise.js new file mode 100644 index 0000000000..ecdeab89fc --- /dev/null +++ b/packages/core/src/actionsApi/initialise.js @@ -0,0 +1,74 @@ +import { + isFunction, filter, map, + uniqBy, keys, difference, + join, reduce, find, +} from 'lodash/fp'; +import { compileExpression, compileCode } from '../common/compileCode'; +import { $ } from '../common'; +import { _executeAction } from './execute'; +import { BadRequestError, NotFoundError } from '../common/errors'; + +export const initialiseActions = (subscribe, behaviourSources, actions, triggers, apis) => { + validateSources(behaviourSources, actions); + subscribeTriggers(subscribe, behaviourSources, actions, triggers, apis); + return createActionsCollection(behaviourSources, actions); +}; + +const createActionsCollection = (behaviourSources, actions) => $(actions, [ + reduce((all, a) => { + all[a.name] = opts => _executeAction(behaviourSources, a, opts); + return all; + }, {}), +]); + +const subscribeTriggers = (subscribe, behaviourSources, actions, triggers, apis) => { + const createOptions = (optionsCreator, eventContext) => { + if (!optionsCreator) return {}; + const create = compileCode(optionsCreator); + return create({ context: eventContext, apis }); + }; + + const shouldRunTrigger = (trigger, eventContext) => { + if (!trigger.condition) return true; + const shouldRun = compileExpression(trigger.condition); + return shouldRun({ context: eventContext }); + }; + + for (let trig of triggers) { + subscribe(trig.eventName, async (ev, ctx) => { + if (shouldRunTrigger(trig, ctx)) { + await _executeAction( + behaviourSources, + find(a => a.name === trig.actionName)(actions), + createOptions(trig.optionsCreator, ctx), + ); + } + }); + } +}; + +const validateSources = (behaviourSources, actions) => { + const declaredSources = $(actions, [ + uniqBy(a => a.behaviourSource), + map(a => a.behaviourSource), + ]); + + const suppliedSources = keys(behaviourSources); + + const missingSources = difference( + declaredSources, suppliedSources, + ); + + if (missingSources.length > 0) { + throw new BadRequestError(`Declared behaviour sources are not supplied: ${join(', ', missingSources)}`); + } + + const missingBehaviours = $(actions, [ + filter(a => !isFunction(behaviourSources[a.behaviourSource][a.behaviourName])), + map(a => `Action: ${a.name} : ${a.behaviourSource}.${a.behaviourName}`), + ]); + + if (missingBehaviours.length > 0) { + throw new NotFoundError(`Missing behaviours: could not find behaviour functions: ${join(', ', missingBehaviours)}`); + } +}; diff --git a/packages/core/src/appInitialise/databaseManager.js b/packages/core/src/appInitialise/databaseManager.js new file mode 100644 index 0000000000..dcd4294794 --- /dev/null +++ b/packages/core/src/appInitialise/databaseManager.js @@ -0,0 +1,27 @@ +import { isNothing } from '../common'; + +export const getDatabaseManager = databaseManager => ({ + createEmptyMasterDb: createEmptyMasterDb(databaseManager), + createEmptyInstanceDb: createEmptyInstanceDb(databaseManager), + getInstanceDbRootConfig: databaseManager.getInstanceDbRootConfig, + masterDatastoreConfig: getMasterDatastoreConfig(databaseManager), + getInstanceDatastoreConfig: getInstanceDatastoreConfig(databaseManager), +}); + +const getMasterDatastoreConfig = databaseManager => databaseManager.getDatastoreConfig('master'); + +const getInstanceDatastoreConfig = databaseManager => (applicationId, instanceId) => databaseManager.getDatastoreConfig( + applicationId, instanceId, +); + +const createEmptyMasterDb = databaseManager => async () => await databaseManager.createEmptyDb('master'); + +const createEmptyInstanceDb = databaseManager => async (applicationId, instanceId) => { + if (isNothing(applicationId)) { throw new Error('CreateDb: application id not supplied'); } + if (isNothing(instanceId)) { throw new Error('CreateDb: instance id not supplied'); } + + return await databaseManager.createEmptyDb( + applicationId, + instanceId, + ); +}; diff --git a/packages/core/src/appInitialise/eventAggregator.js b/packages/core/src/appInitialise/eventAggregator.js new file mode 100644 index 0000000000..62e44b65f3 --- /dev/null +++ b/packages/core/src/appInitialise/eventAggregator.js @@ -0,0 +1,27 @@ +import { has } from 'lodash'; + +const publish = handlers => async (eventName, context = {}) => { + if (!has(handlers, eventName)) return; + + for (const handler of handlers[eventName]) { + await handler(eventName, context); + } +}; + +const subscribe = handlers => (eventName, handler) => { + if (!has(handlers, eventName)) { + handlers[eventName] = []; + } + handlers[eventName].push(handler); +}; + +export const createEventAggregator = () => { + const handlers = {}; + const eventAggregator = ({ + publish: publish(handlers), + subscribe: subscribe(handlers), + }); + return eventAggregator; +}; + +export default createEventAggregator; diff --git a/packages/core/src/appInitialise/inProcIndexer.js b/packages/core/src/appInitialise/inProcIndexer.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/appInitialise/index.js b/packages/core/src/appInitialise/index.js new file mode 100644 index 0000000000..9ffbe122ef --- /dev/null +++ b/packages/core/src/appInitialise/index.js @@ -0,0 +1,36 @@ +import { retry } from '../common/index'; +import { NotFoundError } from '../common/errors'; + +const createJson = originalCreateFile => async (key, obj, retries = 5, delay = 500) => await retry(originalCreateFile, retries, delay, key, JSON.stringify(obj)); + +const createNewFile = originalCreateFile => async (path, content, retries = 5, delay = 500) => await retry(originalCreateFile, retries, delay, path, content); + +const loadJson = datastore => async (key, retries = 5, delay = 500) => { + try { + return await retry(JSON.parse, retries, delay, await datastore.loadFile(key)); + } catch (err) { + throw new NotFoundError(err.message); + } +} + +const updateJson = datastore => async (key, obj, retries = 5, delay = 500) => { + try { + return await retry(datastore.updateFile, retries, delay, key, JSON.stringify(obj)); + } catch (err) { + throw new NotFoundError(err.message); + } +} + +export const setupDatastore = (datastore) => { + const originalCreateFile = datastore.createFile; + datastore.loadJson = loadJson(datastore); + datastore.createJson = createJson(originalCreateFile); + datastore.updateJson = updateJson(datastore); + datastore.createFile = createNewFile(originalCreateFile); + if (datastore.createEmptyDb) { delete datastore.createEmptyDb; } + return datastore; +}; + +export { createEventAggregator } from './eventAggregator'; + +export default setupDatastore; diff --git a/packages/core/src/appInitialise/initialiseData.js b/packages/core/src/appInitialise/initialiseData.js new file mode 100644 index 0000000000..c66429a796 --- /dev/null +++ b/packages/core/src/appInitialise/initialiseData.js @@ -0,0 +1,58 @@ +import { filter } from 'lodash/fp'; +import { configFolder, appDefinitionFile, $ } from '../common'; +import { TRANSACTIONS_FOLDER } from '../transactions/transactionsCommon'; +import { AUTH_FOLDER, USERS_LIST_FILE, ACCESS_LEVELS_FILE } from '../authApi/authCommon'; +import { initialiseRootCollections } from '../collectionApi/initialise'; +import { initialiseIndex } from '../indexing/initialiseIndex'; +import { getFlattenedHierarchy, isGlobalIndex, isSingleRecord } from '../templateApi/hierarchy'; +import { _save } from '../recordApi/save'; +import { getNew } from '../recordApi/getNew'; + +export const initialiseData = async (datastore, applicationDefinition, accessLevels) => { + await datastore.createFolder(configFolder); + await datastore.createJson(appDefinitionFile, applicationDefinition); + + await initialiseRootCollections(datastore, applicationDefinition.hierarchy); + await initialiseRootIndexes(datastore, applicationDefinition.hierarchy); + + await initialiseRootSingleRecords(datastore, applicationDefinition.hierarchy); + + await datastore.createFolder(TRANSACTIONS_FOLDER); + + await datastore.createFolder(AUTH_FOLDER); + + await datastore.createJson(USERS_LIST_FILE, []); + + await datastore.createJson( + ACCESS_LEVELS_FILE, + accessLevels ? accessLevels : { version: 0, levels: [] }); +}; + +const initialiseRootIndexes = async (datastore, hierarchy) => { + const flathierarchy = getFlattenedHierarchy(hierarchy); + const globalIndexes = $(flathierarchy, [ + filter(isGlobalIndex), + ]); + + for (const index of globalIndexes) { + if (!await datastore.exists(index.nodeKey())) { await initialiseIndex(datastore, '', index); } + } +}; + +const initialiseRootSingleRecords = async (datastore, hierachy) => { + const flathierarchy = getFlattenedHierarchy(hierachy); + const singleRecords = $(flathierarchy, [ + filter(isSingleRecord), + ]); + + /* for (let record of singleRecords) { + const result = getNew({ datastore: datastore, hierarchy: appDefinition.hierarchy }) + (record.nodeKey(), + record.name + ); + + _save({ datastore: datastore, hierarchy: appDefinition.hierarchy }, + result + ); + } */ +}; diff --git a/packages/core/src/authApi/authCommon.js b/packages/core/src/authApi/authCommon.js new file mode 100644 index 0000000000..18e3fbd63c --- /dev/null +++ b/packages/core/src/authApi/authCommon.js @@ -0,0 +1,49 @@ +import { clone, find, split } from 'lodash/fp'; +import { joinKey, $ } from '../common'; +// 5 minutes +export const tempCodeExpiryLength = 5 * 60 * 1000; + +export const AUTH_FOLDER = '/.auth'; +export const USERS_LIST_FILE = joinKey(AUTH_FOLDER, 'users.json'); +export const userAuthFile = username => joinKey(AUTH_FOLDER, `auth_${username}.json`); +export const USERS_LOCK_FILE = joinKey(AUTH_FOLDER, 'users_lock'); +export const ACCESS_LEVELS_FILE = joinKey(AUTH_FOLDER, 'access_levels.json'); +export const ACCESS_LEVELS_LOCK_FILE = joinKey(AUTH_FOLDER, 'access_levels_lock'); + +export const permissionTypes = { + CREATE_RECORD: 'create record', + UPDATE_RECORD: 'update record', + READ_RECORD: 'read record', + DELETE_RECORD: 'delete record', + READ_INDEX: 'read index', + MANAGE_INDEX: 'manage index', + MANAGE_COLLECTION: 'manage collection', + WRITE_TEMPLATES: 'write templates', + CREATE_USER: 'create user', + SET_PASSWORD: 'set password', + CREATE_TEMPORARY_ACCESS: 'create temporary access', + ENABLE_DISABLE_USER: 'enable or disable user', + WRITE_ACCESS_LEVELS: 'write access levels', + LIST_USERS: 'list users', + LIST_ACCESS_LEVELS: 'list access levels', + EXECUTE_ACTION: 'execute action', + SET_USER_ACCESS_LEVELS: 'set user access levels', +}; + +export const getUserByName = (users, name) => $(users, [ + find(u => u.name.toLowerCase() === name.toLowerCase()), +]); + +export const stripUserOfSensitiveStuff = (user) => { + const stripped = clone(user); + delete stripped.tempCode; + return stripped; +}; + +export const parseTemporaryCode = fullCode => $(fullCode, [ + split(':'), + parts => ({ + id: parts[1], + code: parts[2], + }), +]); diff --git a/packages/core/src/authApi/authenticate.js b/packages/core/src/authApi/authenticate.js new file mode 100644 index 0000000000..3d1bf05a67 --- /dev/null +++ b/packages/core/src/authApi/authenticate.js @@ -0,0 +1,117 @@ +import { + find, filter, some, + map, flatten, +} from 'lodash/fp'; +import { generate } from 'shortid'; +import { _getUsers } from './getUsers'; +import { + getUserByName, userAuthFile, + parseTemporaryCode, +} from './authCommon'; +import { _loadAccessLevels } from './loadAccessLevels'; +import { + isNothingOrEmpty, $, apiWrapper, events, +} from '../common'; +import { alwaysAuthorized } from './permissions'; + +const dummyHash = '$argon2i$v=19$m=4096,t=3,p=1$UZRo409UYBGjHJS3CV6Uxw$rU84qUqPeORFzKYmYY0ceBLDaPO+JWSH4PfNiKXfIKk'; + +export const authenticate = app => async (username, password) => apiWrapper( + app, + events.authApi.authenticate, + alwaysAuthorized, + { username, password }, + _authenticate, app, username, password, +); + +export const _authenticate = async (app, username, password) => { + if (isNothingOrEmpty(username) || isNothingOrEmpty(password)) { return null; } + + const allUsers = await _getUsers(app); + let user = getUserByName( + allUsers, + username, + ); + + const notAUser = 'not-a-user'; + // continue with non-user - so time to verify remains consistent + // with verification of a valid user + if (!user || !user.enabled) { user = notAUser; } + + let userAuth; + try { + userAuth = await app.datastore.loadJson( + userAuthFile(username), + ); + } catch (_) { + userAuth = { accessLevels: [], passwordHash: dummyHash }; + } + + const permissions = await buildUserPermissions(app, user.accessLevels); + + const verified = await app.crypto.verify( + userAuth.passwordHash, + password, + ); + + if (user === notAUser) { return null; } + + return verified + ? { + ...user, permissions, temp: false, isUser: true, + } + : null; +}; + +export const authenticateTemporaryAccess = app => async (tempAccessCode) => { + if (isNothingOrEmpty(tempAccessCode)) { return null; } + + const temp = parseTemporaryCode(tempAccessCode); + let user = $(await _getUsers(app), [ + find(u => u.temporaryAccessId === temp.id), + ]); + + const notAUser = 'not-a-user'; + if (!user || !user.enabled) { user = notAUser; } + + let userAuth; + try { + userAuth = await app.datastore.loadJson( + userAuthFile(user.name), + ); + } catch (e) { + userAuth = { + temporaryAccessHash: dummyHash, + temporaryAccessExpiryEpoch: (await app.getEpochTime() + 10000), + }; + } + + if (userAuth.temporaryAccessExpiryEpoch < await app.getEpochTime()) { user = notAUser; } + + const tempCode = !temp.code ? generate() : temp.code; + const verified = await app.crypto.verify( + userAuth.temporaryAccessHash, + tempCode, + ); + + if (user === notAUser) { return null; } + + return verified + ? { + ...user, + permissions: [], + temp: true, + isUser: true, + } + : null; +}; + +export const buildUserPermissions = async (app, userAccessLevels) => { + const allAccessLevels = await _loadAccessLevels(app); + + return $(allAccessLevels.levels, [ + filter(l => some(ua => l.name === ua)(userAccessLevels)), + map(l => l.permissions), + flatten, + ]); +}; diff --git a/packages/core/src/authApi/createTemporaryAccess.js b/packages/core/src/authApi/createTemporaryAccess.js new file mode 100644 index 0000000000..d35293f064 --- /dev/null +++ b/packages/core/src/authApi/createTemporaryAccess.js @@ -0,0 +1,79 @@ +import { generate } from 'shortid'; +import { + tempCodeExpiryLength, USERS_LOCK_FILE, + USERS_LIST_FILE, userAuthFile, + getUserByName, +} from './authCommon'; +import { + getLock, isNolock, + releaseLock, +} from '../common/lock'; +import { apiWrapper, events } from '../common'; +import { alwaysAuthorized } from './permissions'; + +export const createTemporaryAccess = app => async userName => apiWrapper( + app, + events.authApi.createTemporaryAccess, + alwaysAuthorized, + { userName }, + _createTemporaryAccess, app, userName, +); + +export const _createTemporaryAccess = async (app, userName) => { + const tempCode = await getTemporaryCode(app); + + const lock = await getLock( + app, USERS_LOCK_FILE, 1000, 2, + ); + + if (isNolock(lock)) { throw new Error('Unable to create temporary access, could not get lock - try again'); } + + try { + const users = await app.datastore.loadJson(USERS_LIST_FILE); + + const user = getUserByName(users, userName); + user.temporaryAccessId = tempCode.temporaryAccessId; + + await app.datastore.updateJson( + USERS_LIST_FILE, + users, + ); + } finally { + await releaseLock(app, lock); + } + + const userAuth = await app.datastore.loadJson( + userAuthFile(userName), + ); + userAuth.temporaryAccessHash = tempCode.temporaryAccessHash; + + userAuth.temporaryAccessExpiryEpoch = tempCode.temporaryAccessExpiryEpoch; + + await app.datastore.updateJson( + userAuthFile(userName), + userAuth, + ); + + return tempCode.tempCode; +}; + +export const getTemporaryCode = async (app) => { + const tempCode = generate() + + generate() + + generate() + + generate(); + + const tempId = generate(); + + return { + temporaryAccessHash: await app.crypto.hash( + tempCode, + ), + temporaryAccessExpiryEpoch: + (await app.getEpochTime()) + tempCodeExpiryLength, + tempCode: `tmp:${tempId}:${tempCode}`, + temporaryAccessId: tempId, + }; +}; + +export const looksLikeTemporaryCode = code => code.startsWith('tmp:'); diff --git a/packages/core/src/authApi/createUser.js b/packages/core/src/authApi/createUser.js new file mode 100644 index 0000000000..6c42e9e2e0 --- /dev/null +++ b/packages/core/src/authApi/createUser.js @@ -0,0 +1,96 @@ +import { join, some } from 'lodash/fp'; +import { validateUser } from './validateUser'; +import { getNewUserAuth } from './getNewUser'; +import { + getLock, isNolock, releaseLock, apiWrapper, events, + insensitiveEquals, isNonEmptyString, +} from '../common'; +import { + USERS_LOCK_FILE, stripUserOfSensitiveStuff, + USERS_LIST_FILE, userAuthFile, +} from './authCommon'; +import { getTemporaryCode } from './createTemporaryAccess'; +import { isValidPassword } from './setPassword'; +import { permission } from './permissions'; +import { BadRequestError } from '../common/errors'; + +export const createUser = app => async (user, password = null) => apiWrapper( + app, + events.authApi.createUser, + permission.createUser.isAuthorized, + { user, password }, + _createUser, app, user, password, +); + +export const _createUser = async (app, user, password = null) => { + const lock = await getLock( + app, USERS_LOCK_FILE, 1000, 2, + ); + + if (isNolock(lock)) { throw new Error('Unable to create user, could not get lock - try again'); } + + const users = await app.datastore.loadJson(USERS_LIST_FILE); + + const userErrors = validateUser(app)([...users, user], user); + if (userErrors.length > 0) { throw new BadRequestError(`User is invalid. ${join('; ')(userErrors)}`); } + + const { auth, tempCode, temporaryAccessId } = await getAccess( + app, password, + ); + user.tempCode = tempCode; + user.temporaryAccessId = temporaryAccessId; + + if (some(u => insensitiveEquals(u.name, user.name))(users)) { + throw new BadRequestError('User already exists'); + } + + users.push( + stripUserOfSensitiveStuff(user), + ); + + await app.datastore.updateJson( + USERS_LIST_FILE, + users, + ); + + try { + await app.datastore.createJson( + userAuthFile(user.name), + auth, + ); + } catch (_) { + await app.datastore.updateJson( + userAuthFile(user.name), + auth, + ); + } + + await releaseLock(app, lock); + + return user; +}; + +const getAccess = async (app, password) => { + const auth = getNewUserAuth(app)(); + + if (isNonEmptyString(password)) { + if (isValidPassword(password)) { + auth.passwordHash = await app.crypto.hash(password); + auth.temporaryAccessHash = ''; + auth.temporaryAccessId = ''; + auth.temporaryAccessExpiryEpoch = 0; + return { auth }; + } + throw new BadRequestError('Password does not meet requirements'); + } else { + const tempAccess = await getTemporaryCode(app); + auth.temporaryAccessHash = tempAccess.temporaryAccessHash; + auth.temporaryAccessExpiryEpoch = tempAccess.temporaryAccessExpiryEpoch; + auth.passwordHash = ''; + return ({ + auth, + tempCode: tempAccess.tempCode, + temporaryAccessId: tempAccess.temporaryAccessId, + }); + } +}; diff --git a/packages/core/src/authApi/enableUser.js b/packages/core/src/authApi/enableUser.js new file mode 100644 index 0000000000..1c18b0ca31 --- /dev/null +++ b/packages/core/src/authApi/enableUser.js @@ -0,0 +1,49 @@ +import { + getLock, + isNolock, releaseLock, +} from '../common/lock'; +import { USERS_LOCK_FILE, USERS_LIST_FILE, getUserByName } from './authCommon'; +import { apiWrapper, events } from '../common'; +import { permission } from './permissions'; +import { NotFoundError } from '../common/errors'; + +export const enableUser = app => async username => apiWrapper( + app, + events.authApi.enableUser, + permission.enableDisableUser.isAuthorized, + { username }, + _enableUser, app, username, +); + +export const disableUser = app => async username => apiWrapper( + app, + events.authApi.disableUser, + permission.enableDisableUser.isAuthorized, + { username }, + _disableUser, app, username, +); + +export const _enableUser = async (app, username) => await toggleUser(app, username, true); + +export const _disableUser = async (app, username) => await toggleUser(app, username, false); + +const toggleUser = async (app, username, enabled) => { + const lock = await getLock(app, USERS_LOCK_FILE, 1000, 1, 0); + + const actionName = enabled ? 'enable' : 'disable'; + + if (isNolock(lock)) { throw new Error(`Could not ${actionName} user - cannot get lock`); } + + try { + const users = await app.datastore.loadJson(USERS_LIST_FILE); + const user = getUserByName(users, username); + if (!user) { throw new NotFoundError(`Could not find user to ${actionName}`); } + + if (user.enabled === !enabled) { + user.enabled = enabled; + await app.datastore.updateJson(USERS_LIST_FILE, users); + } + } finally { + releaseLock(app, lock); + } +}; diff --git a/packages/core/src/authApi/generateFullPermissions.js b/packages/core/src/authApi/generateFullPermissions.js new file mode 100644 index 0000000000..61aaa91127 --- /dev/null +++ b/packages/core/src/authApi/generateFullPermissions.js @@ -0,0 +1,45 @@ +import { + filter, values, each, keys, +} from 'lodash/fp'; +import { permission } from './permissions'; +import { + getFlattenedHierarchy, + isIndex, isRecord, +} from '../templateApi/hierarchy'; +import { $ } from '../common'; + +export const generateFullPermissions = (app) => { + const allNodes = getFlattenedHierarchy(app.hierarchy); + const accessLevel = { permissions: [] }; + + const recordNodes = $(allNodes, [ + filter(isRecord), + ]); + + for (const n of recordNodes) { + permission.createRecord.add(n.nodeKey(), accessLevel); + permission.updateRecord.add(n.nodeKey(), accessLevel); + permission.deleteRecord.add(n.nodeKey(), accessLevel); + permission.readRecord.add(n.nodeKey(), accessLevel); + } + + const indexNodes = $(allNodes, [ + filter(isIndex), + ]); + + for (const n of indexNodes) { + permission.readIndex.add(n.nodeKey(), accessLevel); + } + + for (const a of keys(app.actions)) { + permission.executeAction.add(a, accessLevel); + } + + $(permission, [ + values, + filter(p => !p.isNode), + each(p => p.add(accessLevel)), + ]); + + return accessLevel.permissions; +}; diff --git a/packages/core/src/authApi/getNewAccessLevel.js b/packages/core/src/authApi/getNewAccessLevel.js new file mode 100644 index 0000000000..3b492ef88b --- /dev/null +++ b/packages/core/src/authApi/getNewAccessLevel.js @@ -0,0 +1,5 @@ +export const getNewAccessLevel = () => () => ({ + name: '', + permissions: [], + default:false +}); diff --git a/packages/core/src/authApi/getNewUser.js b/packages/core/src/authApi/getNewUser.js new file mode 100644 index 0000000000..eddb94934c --- /dev/null +++ b/packages/core/src/authApi/getNewUser.js @@ -0,0 +1,31 @@ +import { apiWrapperSync, events } from '../common'; +import { permission } from './permissions'; + +export const getNewUser = app => () => apiWrapperSync( + app, + events.authApi.getNewUser, + permission.createUser.isAuthorized, + {}, + _getNewUser, app, +); + +export const _getNewUser = () => ({ + name: '', + accessLevels: [], + enabled: true, + temporaryAccessId: '', +}); + +export const getNewUserAuth = app => () => apiWrapperSync( + app, + events.authApi.getNewUserAuth, + permission.createUser.isAuthorized, + {}, + _getNewUserAuth, app, +); + +export const _getNewUserAuth = () => ({ + passwordHash: '', + temporaryAccessHash: '', + temporaryAccessExpiryEpoch: 0, +}); diff --git a/packages/core/src/authApi/getUsers.js b/packages/core/src/authApi/getUsers.js new file mode 100644 index 0000000000..231d55a17b --- /dev/null +++ b/packages/core/src/authApi/getUsers.js @@ -0,0 +1,19 @@ +import { map } from 'lodash/fp'; +import { + USERS_LIST_FILE, + stripUserOfSensitiveStuff, +} from './authCommon'; +import { $, apiWrapper, events } from '../common'; +import { permission } from './permissions'; + +export const getUsers = app => async () => apiWrapper( + app, + events.authApi.getUsers, + permission.listUsers.isAuthorized, + {}, + _getUsers, app, +); + +export const _getUsers = async app => $(await app.datastore.loadJson(USERS_LIST_FILE), [ + map(stripUserOfSensitiveStuff), +]); diff --git a/packages/core/src/authApi/index.js b/packages/core/src/authApi/index.js new file mode 100644 index 0000000000..2e70e97dfa --- /dev/null +++ b/packages/core/src/authApi/index.js @@ -0,0 +1,48 @@ +import { + authenticate, + authenticateTemporaryAccess, +} from './authenticate'; +import { createTemporaryAccess } from './createTemporaryAccess'; +import { createUser } from './createUser'; +import { enableUser, disableUser } from './enableUser'; +import { loadAccessLevels } from './loadAccessLevels'; +import { getNewAccessLevel } from './getNewAccessLevel'; +import { getNewUser, getNewUserAuth } from './getNewUser'; +import { getUsers } from './getUsers'; +import { isAuthorized } from './isAuthorized'; +import { saveAccessLevels } from './saveAccessLevels'; +import { + changeMyPassword, + scorePassword, setPasswordFromTemporaryCode, + isValidPassword, +} from './setPassword'; +import { validateUser } from './validateUser'; +import { validateAccessLevels } from './validateAccessLevels'; +import { generateFullPermissions } from './generateFullPermissions'; +import { setUserAccessLevels } from './setUserAccessLevels'; + +export const getAuthApi = app => ({ + authenticate: authenticate(app), + authenticateTemporaryAccess: authenticateTemporaryAccess(app), + createTemporaryAccess: createTemporaryAccess(app), + createUser: createUser(app), + loadAccessLevels: loadAccessLevels(app), + enableUser: enableUser(app), + disableUser: disableUser(app), + getNewAccessLevel: getNewAccessLevel(app), + getNewUser: getNewUser(app), + getNewUserAuth: getNewUserAuth(app), + getUsers: getUsers(app), + saveAccessLevels: saveAccessLevels(app), + isAuthorized: isAuthorized(app), + changeMyPassword: changeMyPassword(app), + setPasswordFromTemporaryCode: setPasswordFromTemporaryCode(app), + scorePassword, + isValidPassword: isValidPassword(app), + validateUser: validateUser(app), + validateAccessLevels: validateAccessLevels(app), + generateFullPermissions: () => generateFullPermissions(app), + setUserAccessLevels: setUserAccessLevels(app), +}); + +export default getAuthApi; diff --git a/packages/core/src/authApi/isAuthorized.js b/packages/core/src/authApi/isAuthorized.js new file mode 100644 index 0000000000..e952a803e3 --- /dev/null +++ b/packages/core/src/authApi/isAuthorized.js @@ -0,0 +1,50 @@ +import { values, includes, some } from 'lodash/fp'; +import { permissionTypes } from './authCommon'; +import { + $, isNothing, apiWrapperSync, events, +} from '../common'; +import { getNodeByKeyOrNodeKey, isNode } from '../templateApi/hierarchy'; +import { alwaysAuthorized } from './permissions'; + +export const isAuthorized = app => (permissionType, resourceKey) => apiWrapperSync( + app, + events.authApi.isAuthorized, + alwaysAuthorized, + { resourceKey, permissionType }, + _isAuthorized, app, permissionType, resourceKey, +); + +export const _isAuthorized = (app, permissionType, resourceKey) => { + if (!app.user) { + return false; + } + + const validType = $(permissionTypes, [ + values, + includes(permissionType), + ]); + + if (!validType) { + return false; + } + + const permMatchesResource = (userperm) => { + const nodeKey = isNothing(resourceKey) + ? null + : isNode(app.hierarchy, resourceKey) + ? getNodeByKeyOrNodeKey( + app.hierarchy, resourceKey, + ).nodeKey() + : resourceKey; + + return (userperm.type === permissionType) + && ( + isNothing(resourceKey) + || nodeKey === userperm.nodeKey + ); + }; + + return $(app.user.permissions, [ + some(permMatchesResource), + ]); +}; diff --git a/packages/core/src/authApi/loadAccessLevels.js b/packages/core/src/authApi/loadAccessLevels.js new file mode 100644 index 0000000000..807c24fc4e --- /dev/null +++ b/packages/core/src/authApi/loadAccessLevels.js @@ -0,0 +1,13 @@ +import { ACCESS_LEVELS_FILE } from './authCommon'; +import { apiWrapper, events } from '../common'; +import { permission } from './permissions'; + +export const loadAccessLevels = app => async () => apiWrapper( + app, + events.authApi.loadAccessLevels, + permission.listAccessLevels.isAuthorized, + {}, + _loadAccessLevels, app, +); + +export const _loadAccessLevels = async app => await app.datastore.loadJson(ACCESS_LEVELS_FILE); diff --git a/packages/core/src/authApi/permissions.js b/packages/core/src/authApi/permissions.js new file mode 100644 index 0000000000..9b34098409 --- /dev/null +++ b/packages/core/src/authApi/permissions.js @@ -0,0 +1,74 @@ +import { permissionTypes } from './authCommon'; +import { isAuthorized } from './isAuthorized'; + +export const temporaryAccessPermissions = () => ([{ type: permissionTypes.SET_PASSWORD }]); + +const nodePermission = type => ({ + add: (nodeKey, accessLevel) => accessLevel.permissions.push({ type, nodeKey }), + isAuthorized: resourceKey => app => isAuthorized(app)(type, resourceKey), + isNode: true, + get: nodeKey => ({ type, nodeKey }), +}); + +const staticPermission = type => ({ + add: accessLevel => accessLevel.permissions.push({ type }), + isAuthorized: app => isAuthorized(app)(type), + isNode: false, + get: () => ({ type }), +}); + +const createRecord = nodePermission(permissionTypes.CREATE_RECORD); + +const updateRecord = nodePermission(permissionTypes.UPDATE_RECORD); + +const deleteRecord = nodePermission(permissionTypes.DELETE_RECORD); + +const readRecord = nodePermission(permissionTypes.READ_RECORD); + +const writeTemplates = staticPermission(permissionTypes.WRITE_TEMPLATES); + +const createUser = staticPermission(permissionTypes.CREATE_USER); + +const setPassword = staticPermission(permissionTypes.SET_PASSWORD); + +const readIndex = nodePermission(permissionTypes.READ_INDEX); + +const manageIndex = staticPermission(permissionTypes.MANAGE_INDEX); + +const manageCollection = staticPermission(permissionTypes.MANAGE_COLLECTION); + +const createTemporaryAccess = staticPermission(permissionTypes.CREATE_TEMPORARY_ACCESS); + +const enableDisableUser = staticPermission(permissionTypes.ENABLE_DISABLE_USER); + +const writeAccessLevels = staticPermission(permissionTypes.WRITE_ACCESS_LEVELS); + +const listUsers = staticPermission(permissionTypes.LIST_USERS); + +const listAccessLevels = staticPermission(permissionTypes.LIST_ACCESS_LEVELS); + +const setUserAccessLevels = staticPermission(permissionTypes.SET_USER_ACCESS_LEVELS); + +const executeAction = nodePermission(permissionTypes.EXECUTE_ACTION); + +export const alwaysAuthorized = () => true; + +export const permission = { + createRecord, + updateRecord, + deleteRecord, + readRecord, + writeTemplates, + createUser, + setPassword, + readIndex, + createTemporaryAccess, + enableDisableUser, + writeAccessLevels, + listUsers, + listAccessLevels, + manageIndex, + manageCollection, + executeAction, + setUserAccessLevels, +}; diff --git a/packages/core/src/authApi/saveAccessLevels.js b/packages/core/src/authApi/saveAccessLevels.js new file mode 100644 index 0000000000..5f525b8e93 --- /dev/null +++ b/packages/core/src/authApi/saveAccessLevels.js @@ -0,0 +1,49 @@ +import { join, map } from 'lodash/fp'; +import { + getLock, releaseLock, $, + isNolock, apiWrapper, events, +} from '../common'; +import { + ACCESS_LEVELS_LOCK_FILE, + ACCESS_LEVELS_FILE, +} from './authCommon'; +import { validateAccessLevels } from './validateAccessLevels'; +import { permission } from './permissions'; + +export const saveAccessLevels = app => async accessLevels => apiWrapper( + app, + events.authApi.saveAccessLevels, + permission.writeAccessLevels.isAuthorized, + { accessLevels }, + _saveAccessLevels, app, accessLevels, +); + +export const _saveAccessLevels = async (app, accessLevels) => { + const validationErrors = validateAccessLevels(app)(accessLevels.levels); + if (validationErrors.length > 0) { + const errs = $(validationErrors, [ + map(e => e.error), + join(', '), + ]); + throw new Error( + `Access Levels Invalid: ${errs}`, + ); + } + + const lock = await getLock( + app, ACCESS_LEVELS_LOCK_FILE, 2000, 2, + ); + + if (isNolock(lock)) { throw new Error('Could not get lock to save access levels'); } + + try { + const existing = await app.datastore.loadJson(ACCESS_LEVELS_FILE); + if (existing.version !== accessLevels.version) { throw new Error('Access levels have already been updated, since you loaded'); } + + accessLevels.version++; + + app.datastore.updateJson(ACCESS_LEVELS_FILE, accessLevels); + } finally { + await releaseLock(app, lock); + } +}; diff --git a/packages/core/src/authApi/setPassword.js b/packages/core/src/authApi/setPassword.js new file mode 100644 index 0000000000..fc6a6e4b9a --- /dev/null +++ b/packages/core/src/authApi/setPassword.js @@ -0,0 +1,153 @@ +import { find } from 'lodash/fp'; +import { userAuthFile, parseTemporaryCode } from './authCommon'; +import { + isSomething, $, apiWrapper, apiWrapperSync, events, +} from '../common'; +import { _getUsers } from './getUsers'; +import { alwaysAuthorized } from './permissions'; + +export const isValidPassword = app => password => apiWrapperSync( + app, + events.authApi.isValidPassword, + alwaysAuthorized, + { password }, + _isValidPassword, app, password, +); + +export const _isValidPassword = (app, password) => scorePassword(password).score > 30; + +export const changeMyPassword = app => async (currentPw, newpassword) => apiWrapper( + app, + events.authApi.changeMyPassword, + alwaysAuthorized, + { currentPw, newpassword }, + _changeMyPassword, app, currentPw, newpassword, +); + +export const _changeMyPassword = async (app, currentPw, newpassword) => { + const existingAuth = await app.datastore.loadJson( + userAuthFile(app.user.name), + ); + + if (isSomething(existingAuth.passwordHash)) { + const verified = await app.crypto.verify( + existingAuth.passwordHash, + currentPw, + ); + + if (verified) { + await await doSet( + app, existingAuth, + app.user.name, newpassword, + ); + return true; + } + } + + return false; +}; + +export const setPasswordFromTemporaryCode = app => async (tempCode, newpassword) => apiWrapper( + app, + events.authApi.setPasswordFromTemporaryCode, + alwaysAuthorized, + { tempCode, newpassword }, + _setPasswordFromTemporaryCode, app, tempCode, newpassword, +); + + +export const _setPasswordFromTemporaryCode = async (app, tempCode, newpassword) => { + const currentTime = await app.getEpochTime(); + + const temp = parseTemporaryCode(tempCode); + + const user = $(await _getUsers(app), [ + find(u => u.temporaryAccessId === temp.id), + ]); + + if (!user) { return false; } + + const existingAuth = await app.datastore.loadJson( + userAuthFile(user.name), + ); + + if (isSomething(existingAuth.temporaryAccessHash) + && existingAuth.temporaryAccessExpiryEpoch > currentTime) { + const verified = await app.crypto.verify( + existingAuth.temporaryAccessHash, + temp.code, + ); + + if (verified) { + await doSet( + app, existingAuth, + user.name, newpassword, + ); + return true; + } + } + + return false; +}; + +const doSet = async (app, auth, username, newpassword) => { + auth.temporaryAccessHash = ''; + auth.temporaryAccessExpiryEpoch = 0; + auth.passwordHash = await app.crypto.hash( + newpassword, + ); + await app.datastore.updateJson( + userAuthFile(username), + auth, + ); +}; + +export const scorePassword = app => password => apiWrapperSync( + app, + events.authApi.scorePassword, + alwaysAuthorized, + { password }, + _scorePassword, password, +); + +export const _scorePassword = (password) => { + // from https://stackoverflow.com/questions/948172/password-strength-meter + // thank you https://stackoverflow.com/users/46617/tm-lv + + let score = 0; + if (!password) { return score; } + + // award every unique letter until 5 repetitions + const letters = new Object(); + for (let i = 0; i < password.length; i++) { + letters[password[i]] = (letters[password[i]] || 0) + 1; + score += 5.0 / letters[password[i]]; + } + + // bonus points for mixing it up + const variations = { + digits: /\d/.test(password), + lower: /[a-z]/.test(password), + upper: /[A-Z]/.test(password), + nonWords: /\W/.test(password), + }; + + let variationCount = 0; + for (const check in variations) { + variationCount += (variations[check] == true) ? 1 : 0; + } + score += (variationCount - 1) * 10; + + const strengthText = score > 80 + ? 'strong' + : score > 60 + ? 'good' + : score >= 30 + ? 'weak' + : 'very weak'; + + return { + score: parseInt(score), + strengthText, + }; +}; diff --git a/packages/core/src/authApi/setUserAccessLevels.js b/packages/core/src/authApi/setUserAccessLevels.js new file mode 100644 index 0000000000..d1eb638044 --- /dev/null +++ b/packages/core/src/authApi/setUserAccessLevels.js @@ -0,0 +1,49 @@ +import { difference, map, join } from 'lodash/fp'; +import { + getLock, isNolock, releaseLock, $, + apiWrapper, events, +} from '../common'; +import { + USERS_LOCK_FILE, ACCESS_LEVELS_FILE, + getUserByName, USERS_LIST_FILE, +} from './authCommon'; +import { permission } from './permissions'; +import { NotFoundError } from '../common/errors'; + +export const setUserAccessLevels = app => async (userName, accessLevels) => apiWrapper( + app, + events.authApi.setUserAccessLevels, + permission.setUserAccessLevels.isAuthorized, + { userName, accessLevels }, + _setUserAccessLevels, app, userName, accessLevels, +); + +export const _setUserAccessLevels = async (app, username, accessLevels) => { + const lock = await getLock(app, USERS_LOCK_FILE, 1000, 1, 0); + + const actualAccessLevels = $( + await app.datastore.loadJson(ACCESS_LEVELS_FILE), + [ + l => l.levels, + map(l => l.name), + ], + ); + + const missing = difference(accessLevels)(actualAccessLevels); + if (missing.length > 0) { + throw new Error(`Invalid access levels supplied: ${join(', ', missing)}`); + } + + if (isNolock(lock)) { throw new Error('Could set user access levels cannot get lock'); } + + try { + const users = await app.datastore.loadJson(USERS_LIST_FILE); + const user = getUserByName(users, username); + if (!user) { throw new NotFoundError(`Could not find user with ${username}`); } + + user.accessLevels = accessLevels; + await app.datastore.updateJson(USERS_LIST_FILE, users); + } finally { + releaseLock(app, lock); + } +}; diff --git a/packages/core/src/authApi/validateAccessLevels.js b/packages/core/src/authApi/validateAccessLevels.js new file mode 100644 index 0000000000..c8695d5051 --- /dev/null +++ b/packages/core/src/authApi/validateAccessLevels.js @@ -0,0 +1,75 @@ +import { + values, includes, map, concat, isEmpty, uniqWith, some, + flatten, filter, +} from 'lodash/fp'; +import { applyRuleSet, makerule } from '../common/validationCommon'; +import { permissionTypes } from './authCommon'; +import { + $, isSomething, insensitiveEquals, + isNonEmptyString, apiWrapperSync, events, +} from '../common'; +import { getNode } from '../templateApi/hierarchy'; +import { alwaysAuthorized } from './permissions'; + +const isAllowedType = t => $(permissionTypes, [ + values, + includes(t), +]); + +const isRecordOrIndexType = t => some(p => p === t)([ + permissionTypes.CREATE_RECORD, + permissionTypes.UPDATE_RECORD, + permissionTypes.DELETE_RECORD, + permissionTypes.READ_RECORD, + permissionTypes.READ_INDEX, + permissionTypes.EXECUTE_ACTION, +]); + + +const permissionRules = app => ([ + makerule('type', 'type must be one of allowed types', + p => isAllowedType(p.type)), + makerule('nodeKey', 'record and index permissions must include a valid nodeKey', + p => (!isRecordOrIndexType(p.type)) + || isSomething(getNode(app.hierarchy, p.nodeKey))), +]); + +const applyPermissionRules = app => applyRuleSet(permissionRules(app)); + +const accessLevelRules = allLevels => ([ + makerule('name', 'name must be set', + l => isNonEmptyString(l.name)), + makerule('name', 'access level names must be unique', + l => isEmpty(l.name) + || filter(a => insensitiveEquals(l.name, a.name))(allLevels).length === 1), +]); + +const applyLevelRules = allLevels => applyRuleSet(accessLevelRules(allLevels)); + +export const validateAccessLevel = app => (allLevels, level) => { + const errs = $(level.permissions, [ + map(applyPermissionRules(app)), + flatten, + concat( + applyLevelRules(allLevels)(level), + ), + ]); + + return errs; +}; + +export const validateAccessLevels = app => allLevels => apiWrapperSync( + app, + events.authApi.validateAccessLevels, + alwaysAuthorized, + { allLevels }, + _validateAccessLevels, app, allLevels, +); + +export const _validateAccessLevels = (app, allLevels) => $(allLevels, [ + map(l => validateAccessLevel(app)(allLevels, l)), + flatten, + uniqWith((x, y) => x.field === y.field + && x.item === y.item + && x.error === y.error), +]); diff --git a/packages/core/src/authApi/validateUser.js b/packages/core/src/authApi/validateUser.js new file mode 100644 index 0000000000..3ee8660eb3 --- /dev/null +++ b/packages/core/src/authApi/validateUser.js @@ -0,0 +1,39 @@ +import { + map, uniqWith, + flatten, filter, +} from 'lodash/fp'; +import { applyRuleSet, makerule } from '../common/validationCommon'; +import { + $, insensitiveEquals, apiWrapper, events, + isNonEmptyString, all, +} from '../common'; +import { alwaysAuthorized } from './permissions'; + +const userRules = allUsers => [ + makerule('name', 'username must be set', + u => isNonEmptyString(u.name)), + makerule('accessLevels', 'user must have at least one access level', + u => u.accessLevels.length > 0), + makerule('name', 'username must be unique', + u => filter(u2 => insensitiveEquals(u2.name, u.name))(allUsers).length === 1), + makerule('accessLevels', 'access levels must only contain stings', + u => all(isNonEmptyString)(u.accessLevels)), +]; + +export const validateUser = () => (allusers, user) => applyRuleSet(userRules(allusers))(user); + +export const validateUsers = app => allUsers => apiWrapper( + app, + events.authApi.validateUsers, + alwaysAuthorized, + { allUsers }, + _validateUsers, app, allUsers, +); + +export const _validateUsers = (app, allUsers) => $(allUsers, [ + map(l => validateUser(app)(allUsers, l)), + flatten, + uniqWith((x, y) => x.field === y.field + && x.item === y.item + && x.error === y.error), +]); diff --git a/packages/core/src/collectionApi/delete.js b/packages/core/src/collectionApi/delete.js new file mode 100644 index 0000000000..e2133f31a4 --- /dev/null +++ b/packages/core/src/collectionApi/delete.js @@ -0,0 +1,79 @@ +import { includes } from 'lodash/fp'; +import { getNodeForCollectionPath } from '../templateApi/hierarchy'; +import { + safeKey, apiWrapper, + events, joinKey, +} from '../common'; +import { _deleteRecord } from '../recordApi/delete'; +import { getAllIdsIterator, getAllIdsShardKey } from '../indexing/allIds'; +import { permission } from '../authApi/permissions'; + +export const deleteCollection = (app, disableCleanup = false) => async key => apiWrapper( + app, + events.collectionApi.delete, + permission.manageCollection.isAuthorized, + { key }, + _deleteCollection, app, key, disableCleanup, +); + + +export const _deleteCollection = async (app, key, disableCleanup) => { + key = safeKey(key); + const node = getNodeForCollectionPath(app.hierarchy)(key); + + await deleteRecords(app, key); + await deleteAllIdsFolders(app, node, key); + await deleteCollectionFolder(app, key); + if (!disableCleanup) { await app.cleanupTransactions(); } +}; + +const deleteCollectionFolder = async (app, key) => await app.datastore.deleteFolder(key); + + +const deleteAllIdsFolders = async (app, node, key) => { + await app.datastore.deleteFolder( + joinKey( + key, 'allids', + node.nodeId, + ), + ); + + await app.datastore.deleteFolder( + joinKey(key, 'allids'), + ); +}; + +const deleteRecords = async (app, key) => { + const deletedAllIdsShards = []; + const deleteAllIdsShard = async (recordId) => { + const shardKey = getAllIdsShardKey( + app.hierarchy, key, recordId, + ); + + if (includes(shardKey)(deletedAllIdsShards)) { + return; + } + + deletedAllIdsShards.push(shardKey); + + await app.datastore.deleteFile(shardKey); + }; + + const iterate = await getAllIdsIterator(app)(key); + + let ids = await iterate(); + while (!ids.done) { + if (ids.result.collectionKey === key) { + for (const id of ids.result.ids) { + await _deleteRecord( + app, + joinKey(key, id), + true, + ); + await deleteAllIdsShard(id); + } + } + + ids = await iterate(); + } +}; diff --git a/packages/core/src/collectionApi/getAllowedRecordTypes.js b/packages/core/src/collectionApi/getAllowedRecordTypes.js new file mode 100644 index 0000000000..e559b825cc --- /dev/null +++ b/packages/core/src/collectionApi/getAllowedRecordTypes.js @@ -0,0 +1,19 @@ +import { getNodeForCollectionPath } from '../templateApi/hierarchy'; +import { + isNothing, safeKey, apiWrapperSync, events, +} from '../common'; +import { alwaysAuthorized } from '../authApi/permissions'; + +export const getAllowedRecordTypes = app => key => apiWrapperSync( + app, + events.collectionApi.getAllowedRecordTypes, + alwaysAuthorized, + { key }, + _getAllowedRecordTypes, app, key, +); + +const _getAllowedRecordTypes = (app, key) => { + key = safeKey(key); + const node = getNodeForCollectionPath(app.hierarchy)(key); + return isNothing(node) ? [] : [node.name]; +}; diff --git a/packages/core/src/collectionApi/index.js b/packages/core/src/collectionApi/index.js new file mode 100644 index 0000000000..756aa021f0 --- /dev/null +++ b/packages/core/src/collectionApi/index.js @@ -0,0 +1,11 @@ +import { getAllIdsIterator } from '../indexing/allIds'; +import { getAllowedRecordTypes } from './getAllowedRecordTypes'; +import { deleteCollection } from './delete'; + +export const getCollectionApi = app => ({ + getAllowedRecordTypes: getAllowedRecordTypes(app), + getAllIdsIterator: getAllIdsIterator(app), + delete: deleteCollection(app), +}); + +export default getCollectionApi; diff --git a/packages/core/src/collectionApi/initialise.js b/packages/core/src/collectionApi/initialise.js new file mode 100644 index 0000000000..a566776269 --- /dev/null +++ b/packages/core/src/collectionApi/initialise.js @@ -0,0 +1,61 @@ +import { filter } from 'lodash/fp'; +import { + getFlattenedHierarchy, + isCollectionRecord, + isRoot, + getExactNodeForPath, +} from '../templateApi/hierarchy'; +import { $, allTrue, joinKey } from '../common'; + +const ensureCollectionIsInitialised = async (datastore, node, parentKey) => { + if (!await datastore.exists(parentKey)) { + await datastore.createFolder(parentKey); + await datastore.createFolder( + joinKey(parentKey, 'allids'), + ); + await datastore.createFolder( + joinKey( + parentKey, + 'allids', + node.nodeId.toString(), + ), + ); + } +}; + +export const initialiseRootCollections = async (datastore, hierarchy) => { + const rootCollectionRecord = allTrue( + n => isRoot(n.parent()), + isCollectionRecord, + ); + + const flathierarchy = getFlattenedHierarchy(hierarchy); + + const collectionRecords = $(flathierarchy, [ + filter(rootCollectionRecord), + ]); + + for (const col of collectionRecords) { + await ensureCollectionIsInitialised( + datastore, + col, + col.collectionPathRegx(), + ); + } +}; + +export const initialiseChildCollections = async (app, recordKey) => { + const childCollectionRecords = $(recordKey, [ + getExactNodeForPath(app.hierarchy), + n => n.children, + filter(isCollectionRecord), + ]); + + for (const child of childCollectionRecords) { + await ensureCollectionIsInitialised( + app.datastore, + child, + joinKey(recordKey, child.collectionName), + ); + } +}; diff --git a/packages/core/src/common/apiWrapper.js b/packages/core/src/common/apiWrapper.js new file mode 100644 index 0000000000..f27413f14d --- /dev/null +++ b/packages/core/src/common/apiWrapper.js @@ -0,0 +1,116 @@ +import { cloneDeep, isUndefined } from 'lodash/fp'; +import { generate } from 'shortid'; +import { UnauthorisedError } from './errors'; + +export const apiWrapper = async (app, eventNamespace, isAuthorized, eventContext, func, ...params) => { + pushCallStack(app, eventNamespace); + + if (!isAuthorized(app)) { + handleNotAuthorized(app, eventContext, eventNamespace); + return; + } + + const startDate = Date.now(); + const elapsed = () => (Date.now() - startDate); + + try { + await app.publish( + eventNamespace.onBegin, + eventContext, + ); + + const result = await func(...params); + + await publishComplete(app, eventContext, eventNamespace, elapsed, result); + return result; + } catch (error) { + await publishError(app, eventContext, eventNamespace, elapsed, error); + throw error; + } +}; + +export const apiWrapperSync = (app, eventNamespace, isAuthorized, eventContext, func, ...params) => { + pushCallStack(app, eventNamespace); + + if (!isAuthorized(app)) { + handleNotAuthorized(app, eventContext, eventNamespace); + return; + } + + const startDate = Date.now(); + const elapsed = () => (Date.now() - startDate); + + try { + app.publish( + eventNamespace.onBegin, + eventContext, + ); + + const result = func(...params); + + publishComplete(app, eventContext, eventNamespace, elapsed, result); + return result; + } catch (error) { + publishError(app, eventContext, eventNamespace, elapsed, error); + throw error; + } +}; + +const handleNotAuthorized = (app, eventContext, eventNamespace) => { + const err = new UnauthorisedError(`Unauthorized: ${eventNamespace}`); + publishError(app, eventContext, eventNamespace, () => 0, err); + throw err; +}; + +const pushCallStack = (app, eventNamespace, seedCallId) => { + const callId = generate(); + + const createCallStack = () => ({ + seedCallId: !isUndefined(seedCallId) + ? seedCallId + : callId, + threadCallId: callId, + stack: [], + }); + + if (isUndefined(app.calls)) { + app.calls = createCallStack(); + } + + app.calls.stack.push({ + namespace: eventNamespace, + callId, + }); +}; + +const popCallStack = (app) => { + app.calls.stack.pop(); + if (app.calls.stack.length === 0) { + delete app.calls; + } +}; + +const publishError = async (app, eventContext, eventNamespace, elapsed, err) => { + const ctx = cloneDeep(eventContext); + ctx.error = err; + ctx.elapsed = elapsed(); + await app.publish( + eventNamespace.onError, + ctx, + ); + popCallStack(app); +}; + +const publishComplete = async (app, eventContext, eventNamespace, elapsed, result) => { + const endcontext = cloneDeep(eventContext); + endcontext.result = result; + endcontext.elapsed = elapsed(); + await app.publish( + eventNamespace.onComplete, + endcontext, + ); + popCallStack(app); + return result; +}; + +export default apiWrapper; diff --git a/packages/core/src/common/compileCode.js b/packages/core/src/common/compileCode.js new file mode 100644 index 0000000000..f6b56e317d --- /dev/null +++ b/packages/core/src/common/compileCode.js @@ -0,0 +1,30 @@ +import { + compileExpression as cExp, + compileCode as cCode +} from '@nx-js/compiler-util'; + +export const compileCode = code => { + let func; + + try { + func = cCode(code); + } catch(e) { + e.message = `Error compiling code : ${code} : ${e.message}`; + throw e; + } + + return func; +} + +export const compileExpression = code => { + let func; + + try { + func = cExp(code); + } catch(e) { + e.message = `Error compiling expression : ${code} : ${e.message}`; + throw e; + } + + return func; +} diff --git a/packages/core/src/common/errors.js b/packages/core/src/common/errors.js new file mode 100644 index 0000000000..4d9c21788e --- /dev/null +++ b/packages/core/src/common/errors.js @@ -0,0 +1,34 @@ +export class BadRequestError extends Error { + constructor(message) { + super(message); + this.httpStatusCode = 400; + } +} + +export class UnauthorisedError extends Error { + constructor(message) { + super(message); + this.httpStatusCode = 401; + } +} + +export class ForbiddenError extends Error { + constructor(message) { + super(message); + this.httpStatusCode = 403; + } +} + +export class NotFoundError extends Error { + constructor(message) { + super(message); + this.httpStatusCode = 404; + } +} + +export class ConflictError extends Error { + constructor(message) { + super(message); + this.httpStatusCode = 409; + } +} \ No newline at end of file diff --git a/packages/core/src/common/events.js b/packages/core/src/common/events.js new file mode 100644 index 0000000000..39e5cb3d91 --- /dev/null +++ b/packages/core/src/common/events.js @@ -0,0 +1,93 @@ +import { union, reduce } from 'lodash/fp'; + +const commonPlus = extra => union(['onBegin', 'onComplete', 'onError'])(extra); + +const common = () => commonPlus([]); + +const _events = { + recordApi: { + save: commonPlus([ + 'onInvalid', + 'onRecordUpdated', + 'onRecordCreated']), + delete: common(), + getContext: common(), + getNew: common(), + load: common(), + validate: common(), + uploadFile: common(), + downloadFile: common(), + }, + indexApi: { + buildIndex: common(), + listItems: common(), + delete: common(), + aggregates: common(), + }, + collectionApi: { + getAllowedRecordTypes: common(), + initialise: common(), + delete: common(), + }, + authApi: { + authenticate: common(), + authenticateTemporaryAccess: common(), + createTemporaryAccess: common(), + createUser: common(), + enableUser: common(), + disableUser: common(), + loadAccessLevels: common(), + getNewAccessLevel: common(), + getNewUser: common(), + getNewUserAuth: common(), + getUsers: common(), + saveAccessLevels: common(), + isAuthorized: common(), + changeMyPassword: common(), + setPasswordFromTemporaryCode: common(), + scorePassword: common(), + isValidPassword: common(), + validateUser: common(), + validateAccessLevels: common(), + setUserAccessLevels: common(), + }, + templateApi: { + saveApplicationHierarchy: common(), + saveActionsAndTriggers: common(), + }, + actionsApi: { + execute: common(), + }, +}; + +const _eventsList = []; + +const makeEvent = (area, method, name) => `${area}:${method}:${name}`; + +for (const areaKey in _events) { + for (const methodKey in _events[areaKey]) { + _events[areaKey][methodKey] = reduce((obj, s) => { + obj[s] = makeEvent(areaKey, methodKey, s); + return obj; + }, + {})(_events[areaKey][methodKey]); + } +} + + +for (const areaKey in _events) { + for (const methodKey in _events[areaKey]) { + for (const name in _events[areaKey][methodKey]) { + _eventsList.push( + _events[areaKey][methodKey][name], + ); + } + } +} + + +export const events = _events; + +export const eventsList = _eventsList; + +export default { events: _events, eventsList: _eventsList }; diff --git a/packages/core/src/common/index.js b/packages/core/src/common/index.js new file mode 100644 index 0000000000..677bf3521f --- /dev/null +++ b/packages/core/src/common/index.js @@ -0,0 +1,265 @@ +import { + isUndefined, isNaN, isNull, + reduce, constant, head, isEmpty, + tail, findIndex, startsWith, join, + dropRight, flow, takeRight, trim, + split, includes, replace, isArray, + isString, isInteger, isDate, toNumber, +} from 'lodash'; +import { some } from 'lodash/fp'; +import { events, eventsList } from './events'; +import { apiWrapper } from './apiWrapper'; +import { + getLock, NO_LOCK, + isNolock +} from './lock'; + +// this is the combinator function +export const $$ = (...funcs) => arg => flow(funcs)(arg); + +// this is the pipe function +export const $ = (arg, funcs) => $$(...funcs)(arg); + +export const keySep = '/'; +const trimKeySep = str => trim(str, keySep); +const splitByKeySep = str => split(str, keySep); +export const safeKey = key => replace(`${keySep}${trimKeySep(key)}`, `${keySep}${keySep}`, keySep); +export const joinKey = (...strs) => { + const paramsOrArray = strs.length === 1 & isArray(strs[0]) + ? strs[0] : strs; + return safeKey(join(paramsOrArray, keySep)); +}; +export const splitKey = $$(trimKeySep, splitByKeySep); +export const getDirFomKey = $$(splitKey, dropRight, p => joinKey(...p)); +export const getFileFromKey = $$(splitKey, takeRight, head); + +export const configFolder = `${keySep}.config`; +export const fieldDefinitions = joinKey(configFolder, 'fields.json'); +export const templateDefinitions = joinKey(configFolder, 'templates.json'); +export const appDefinitionFile = joinKey(configFolder, 'appDefinition.json'); +export const dirIndex = folderPath => joinKey(configFolder, 'dir', ...splitKey(folderPath), 'dir.idx'); +export const getIndexKeyFromFileKey = $$(getDirFomKey, dirIndex); + +export const ifExists = (val, exists, notExists) => (isUndefined(val) + ? isUndefined(notExists) ? (() => { })() : notExists() + : exists()); + +export const getOrDefault = (val, defaultVal) => ifExists(val, () => val, () => defaultVal); + +export const not = func => val => !func(val); +export const isDefined = not(isUndefined); +export const isNonNull = not(isNull); +export const isNotNaN = not(isNaN); + +export const allTrue = (...funcArgs) => val => reduce(funcArgs, + (result, conditionFunc) => (isNull(result) || result == true) && conditionFunc(val), + null); + +export const anyTrue = (...funcArgs) => val => reduce(funcArgs, + (result, conditionFunc) => result == true || conditionFunc(val), + null); + +export const insensitiveEquals = (str1, str2) => str1.trim().toLowerCase() === str2.trim().toLowerCase(); + +export const isSomething = allTrue(isDefined, isNonNull, isNotNaN); +export const isNothing = not(isSomething); +export const isNothingOrEmpty = v => isNothing(v) || isEmpty(v); +export const somethingOrGetDefault = getDefaultFunc => val => (isSomething(val) ? val : getDefaultFunc()); +export const somethingOrDefault = (val, defaultVal) => somethingOrGetDefault(constant(defaultVal))(val); + +export const mapIfSomethingOrDefault = (mapFunc, defaultVal) => val => (isSomething(val) ? mapFunc(val) : defaultVal); + +export const mapIfSomethingOrBlank = mapFunc => mapIfSomethingOrDefault(mapFunc, ''); + +export const none = predicate => collection => !some(predicate)(collection); + +export const all = predicate => collection => none(v => !predicate(v))(collection); + +export const isNotEmpty = ob => !isEmpty(ob); +export const isAsync = fn => fn.constructor.name === 'AsyncFunction'; +export const isNonEmptyArray = allTrue(isArray, isNotEmpty); +export const isNonEmptyString = allTrue(isString, isNotEmpty); +export const tryOr = failFunc => (func, ...args) => { + try { + return func.apply(null, ...args); + } catch (_) { + return failFunc(); + } +}; + +export const tryAwaitOr = failFunc => async (func, ...args) => { + try { + return await func.apply(null, ...args); + } catch (_) { + return await failFunc(); + } +}; + +export const defineError = (func, errorPrefix) => { + try { + return func(); + } catch (err) { + err.message = `${errorPrefix} : ${err.message}`; + throw err; + } +}; + +export const tryOrIgnore = tryOr(() => { }); +export const tryAwaitOrIgnore = tryAwaitOr(async () => { }); +export const causesException = (func) => { + try { + func(); + return false; + } catch (e) { + return true; + } +}; + +export const executesWithoutException = func => !causesException(func); + +export const handleErrorWith = returnValInError => tryOr(constant(returnValInError)); + +export const handleErrorWithUndefined = handleErrorWith(undefined); + +export const switchCase = (...cases) => (value) => { + const nextCase = () => head(cases)[0](value); + const nextResult = () => head(cases)[1](value); + + if (isEmpty(cases)) return; // undefined + if (nextCase() === true) return nextResult(); + return switchCase(...tail(cases))(value); +}; + +export const isValue = val1 => val2 => (val1 === val2); +export const isOneOf = (...vals) => val => includes(vals, val); +export const defaultCase = constant(true); +export const memberMatches = (member, match) => obj => match(obj[member]); + + +export const StartsWith = searchFor => searchIn => startsWith(searchIn, searchFor); + +export const contains = val => array => (findIndex(array, v => v === val) > -1); + +export const getHashCode = (s) => { + let hash = 0; let i; let char; let + l; + if (s.length == 0) return hash; + for (i = 0, l = s.length; i < l; i++) { + char = s.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash |= 0; // Convert to 32bit integer + } + + // converting to string, but dont want a "-" prefixed + if (hash < 0) { return `n${(hash * -1).toString()}`; } + return hash.toString(); +}; + +// thanks to https://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/ +export const awEx = async (promise) => { + try { + const result = await promise; + return [undefined, result]; + } catch (error) { + return [error, undefined]; + } +}; + +export const isSafeInteger = n => isInteger(n) + && n <= Number.MAX_SAFE_INTEGER + && n >= 0 - Number.MAX_SAFE_INTEGER; + +export const toDateOrNull = s => (isNull(s) ? null + : isDate(s) ? s : new Date(s)); +export const toBoolOrNull = s => (isNull(s) ? null + : s === 'true' || s === true); +export const toNumberOrNull = s => (isNull(s) ? null + : toNumber(s)); + +export const isArrayOfString = opts => isArray(opts) && all(isString)(opts); + +export const pause = async duration => new Promise(res => setTimeout(res, duration)); + +export const retry = async (fn, retries, delay, ...args) => { + try { + return await fn(...args); + } catch (err) { + if (retries > 1) { + return await pause(delay).then(async () => await retry(fn, (retries - 1), delay, ...args)); + } + throw err; + } +}; + +export { events } from './events'; +export { apiWrapper, apiWrapperSync } from './apiWrapper'; +export { + getLock, NO_LOCK, releaseLock, + extendLock, isNolock, +} from './lock'; + +export default { + ifExists, + getOrDefault, + isDefined, + isNonNull, + isNotNaN, + allTrue, + isSomething, + mapIfSomethingOrDefault, + mapIfSomethingOrBlank, + configFolder, + fieldDefinitions, + isNothing, + not, + switchCase, + defaultCase, + StartsWith, + contains, + templateDefinitions, + handleErrorWith, + handleErrorWithUndefined, + tryOr, + tryOrIgnore, + tryAwaitOr, + tryAwaitOrIgnore, + dirIndex, + keySep, + $, + $$, + getDirFomKey, + getFileFromKey, + splitKey, + somethingOrDefault, + getIndexKeyFromFileKey, + joinKey, + somethingOrGetDefault, + appDefinitionFile, + isValue, + all, + isOneOf, + memberMatches, + defineError, + anyTrue, + isNonEmptyArray, + causesException, + executesWithoutException, + none, + getHashCode, + awEx, + apiWrapper, + events, + eventsList, + isNothingOrEmpty, + isSafeInteger, + toNumber, + toDate: toDateOrNull, + toBool: toBoolOrNull, + isArrayOfString, + getLock, + NO_LOCK, + isNolock, + insensitiveEquals, + pause, + retry, +}; diff --git a/packages/core/src/common/lock.js b/packages/core/src/common/lock.js new file mode 100644 index 0000000000..ebc280281f --- /dev/null +++ b/packages/core/src/common/lock.js @@ -0,0 +1,99 @@ +import { split } from 'lodash/fp'; +import { $ } from './index'; + +const lockOverlapMilliseconds = 10; + +export const getLock = async (app, lockFile, timeoutMilliseconds, maxLockRetries, retryCount = 0) => { + try { + const timeout = (await app.getEpochTime()) + + timeoutMilliseconds; + + const lock = { + timeout, + key: lockFile, + totalTimeout: timeoutMilliseconds, + }; + + await app.datastore.createFile( + lockFile, + getLockFileContent( + lock.totalTimeout, + lock.timeout, + ), + ); + + return lock; + } catch (e) { + if (retryCount == maxLockRetries) { return NO_LOCK; } + + const lock = parseLockFileContent( + lockFile, + await app.datastore.loadFile(lockFile), + ); + + const currentEpochTime = await app.getEpochTime(); + + if (currentEpochTime < lock.timeout) { + return NO_LOCK; + } + + try { + await app.datastore.deleteFile(lockFile); + } catch (_) { + //empty + } + + await sleepForRetry(); + + return await getLock( + app, lockFile, timeoutMilliseconds, + maxLockRetries, retryCount + 1, + ); + } +}; + +export const getLockFileContent = (totalTimeout, epochTime) => `${totalTimeout}:${epochTime.toString()}`; + +const parseLockFileContent = (key, content) => $(content, [ + split(':'), + parts => ({ + totalTimeout: new Number(parts[0]), + timeout: new Number(parts[1]), + key, + }), +]); + +export const releaseLock = async (app, lock) => { + const currentEpochTime = await app.getEpochTime(); + // only release if not timedout + if (currentEpochTime < (lock.timeout - lockOverlapMilliseconds)) { + try { + await app.datastore.deleteFile(lock.key); + } catch (_) { + //empty + } + } +}; + +export const extendLock = async (app, lock) => { + const currentEpochTime = await app.getEpochTime(); + // only release if not timedout + if (currentEpochTime < (lock.timeout - lockOverlapMilliseconds)) { + try { + lock.timeout = currentEpochTime + lock.timeoutMilliseconds; + await app.datastore.updateFile( + lock.key, + getLockFileContent(lock.totalTimeout, lock.timeout), + ); + return lock; + } catch (_) { + //empty + } + } + return NO_LOCK; +}; + +export const NO_LOCK = 'no lock'; +export const isNolock = id => id === NO_LOCK; + +const sleepForRetry = () => new Promise(resolve => setTimeout(resolve, lockOverlapMilliseconds)); diff --git a/packages/core/src/common/validationCommon.js b/packages/core/src/common/validationCommon.js new file mode 100644 index 0000000000..563d6486de --- /dev/null +++ b/packages/core/src/common/validationCommon.js @@ -0,0 +1,17 @@ +import { filter, map } from 'lodash/fp'; +import { $, isSomething } from './index'; + +export const stringNotEmpty = s => isSomething(s) && s.trim().length > 0; + +export const makerule = (field, error, isValid) => ({ field, error, isValid }); + +export const validationError = (rule, item) => ({ ...rule, item }); + +export const applyRuleSet = ruleSet => itemToValidate => $(ruleSet, [ + map(applyRule(itemToValidate)), + filter(isSomething), +]); + +export const applyRule = itemTovalidate => rule => (rule.isValid(itemTovalidate) + ? null + : validationError(rule, itemTovalidate)); diff --git a/packages/core/src/index.js b/packages/core/src/index.js new file mode 100644 index 0000000000..0a0e46f274 --- /dev/null +++ b/packages/core/src/index.js @@ -0,0 +1,113 @@ +import getRecordApi from "./recordApi"; +import getCollectionApi from "./collectionApi"; +import getIndexApi from "./indexApi"; +import getTemplateApi from "./templateApi"; +import getAuthApi from "./authApi"; +import getActionsApi from "./actionsApi"; +import {setupDatastore, createEventAggregator} from "./appInitialise"; +import {initialiseActions} from "./actionsApi/initialise" +import {isSomething} from "./common"; +import {cleanup} from "./transactions/cleanup"; +import {generateFullPermissions} from "./authApi/generateFullPermissions"; +import {getApplicationDefinition} from "./templateApi/getApplicationDefinition"; +import common from "./common"; +import {getBehaviourSources} from "./templateApi/getBehaviourSources"; +import hierarchy from "./templateApi/hierarchy"; + +export const getAppApis = async (store, behaviourSources = null, + cleanupTransactions = null, + getEpochTime = null, + crypto = null, + appDefinition = null) => { + + store = setupDatastore(store); + + if(!appDefinition) + appDefinition = await getApplicationDefinition(store)(); + + if(!behaviourSources) + behaviourSources = await getBehaviourSources(store); + + const eventAggregator = createEventAggregator(); + + const app = { + datastore:store, + crypto, + publish:eventAggregator.publish, + hierarchy:appDefinition.hierarchy, + actions:appDefinition.actions + }; + + const templateApi = getTemplateApi(app); + + app.cleanupTransactions = isSomething(cleanupTransactions) + ? cleanupTransactions + : async () => await cleanup(app); + + app.getEpochTime = isSomething(getEpochTime) + ? getEpochTime + : async () => (new Date()).getTime(); + + const recordApi = getRecordApi(app); + const collectionApi = getCollectionApi(app); + const indexApi = getIndexApi(app); + const authApi = getAuthApi(app); + const actionsApi = getActionsApi(app); + + const authenticateAs = async (username, password) => { + app.user = await authApi.authenticate(username, password); + }; + + const withFullAccess = () => { + app.user = { + name: "app", + permissions : generateFullPermissions(app), + isUser:false, + temp:false + } + }; + + const asUser = (user) => { + app.user = user + }; + + + + let apis = { + recordApi, + templateApi, + collectionApi, + indexApi, + authApi, + actionsApi, + subscribe: eventAggregator.subscribe, + authenticateAs, + withFullAccess, + asUser + }; + + apis.actions = initialiseActions( + eventAggregator.subscribe, + behaviourSources, + appDefinition.actions, + appDefinition.triggers, + apis); + + + return apis; +}; + +export {events, eventsList} from "./common/events"; +export {getTemplateApi} from "./templateApi"; +export {getRecordApi} from "./recordApi"; +export {getCollectionApi} from "./collectionApi"; +export {getAuthApi} from "./authApi"; +export {getIndexApi} from "./indexApi"; +export {setupDatastore} from "./appInitialise"; +export {getActionsApi} from "./actionsApi"; +export {initialiseData} from "./appInitialise/initialiseData"; +export {getDatabaseManager} from "./appInitialise/databaseManager"; +export {hierarchy}; +export {common}; + +export default getAppApis; \ No newline at end of file diff --git a/packages/core/src/indexApi/aggregates.js b/packages/core/src/indexApi/aggregates.js new file mode 100644 index 0000000000..2020a4dda8 --- /dev/null +++ b/packages/core/src/indexApi/aggregates.js @@ -0,0 +1,168 @@ +import { has, isNumber, isUndefined } from 'lodash/fp'; +import { compileExpression, compileCode } from '@nx-js/compiler-util'; +import { + safeKey, apiWrapper, + events, isNonEmptyString, +} from '../common'; +import { iterateIndex } from '../indexing/read'; +import { + getUnshardedIndexDataKey, + getShardKeysInRange, +} from '../indexing/sharding'; +import { + getExactNodeForPath, isIndex, + isShardedIndex, +} from '../templateApi/hierarchy'; +import { CONTINUE_READING_RECORDS } from '../indexing/serializer'; +import { permission } from '../authApi/permissions'; +import { BadRequestError } from '../common/errors'; + +export const aggregates = app => async (indexKey, rangeStartParams = null, rangeEndParams = null) => apiWrapper( + app, + events.indexApi.aggregates, + permission.readIndex.isAuthorized(indexKey), + { indexKey, rangeStartParams, rangeEndParams }, + _aggregates, app, indexKey, rangeStartParams, rangeEndParams, +); + +const _aggregates = async (app, indexKey, rangeStartParams, rangeEndParams) => { + indexKey = safeKey(indexKey); + const indexNode = getExactNodeForPath(app.hierarchy)(indexKey); + + if (!isIndex(indexNode)) { throw new BadRequestError('supplied key is not an index'); } + + if (isShardedIndex(indexNode)) { + const shardKeys = await getShardKeysInRange( + app, indexKey, rangeStartParams, rangeEndParams, + ); + let aggregateResult = null; + for (const k of shardKeys) { + const shardResult = await getAggregates(app.hierarchy, app.datastore, indexNode, k); + if (aggregateResult === null) { + aggregateResult = shardResult; + } else { + aggregateResult = mergeShardAggregate( + aggregateResult, + shardResult, + ); + } + } + return aggregateResult; + } + return await getAggregates( + app.hierarchy, + app.datastore, + indexNode, + getUnshardedIndexDataKey(indexKey), + ); +}; + +const mergeShardAggregate = (totals, shard) => { + const mergeGrouping = (tot, shr) => { + tot.count += shr.count; + for (const aggName in tot) { + if (aggName === 'count') continue; + const totagg = tot[aggName]; + const shragg = shr[aggName]; + totagg.sum += shragg.sum; + totagg.max = totagg.max > shragg.max + ? totagg.max + : shragg.max; + totagg.min = totagg.min < shragg.min + ? totagg.min + : shragg.min; + totagg.mean = totagg.sum / tot.count; + } + return tot; + }; + + for (const aggGroupDef in totals) { + for (const grouping in shard[aggGroupDef]) { + const groupingTotal = totals[aggGroupDef][grouping]; + totals[aggGroupDef][grouping] = isUndefined(groupingTotal) + ? shard[aggGroupDef][grouping] + : mergeGrouping( + totals[aggGroupDef][grouping], + shard[aggGroupDef][grouping], + ); + } + } + + return totals; +}; + +const getAggregates = async (hierarchy, datastore, index, indexedDataKey) => { + const aggregateResult = {}; + const doRead = iterateIndex( + async item => { + applyItemToAggregateResult( + index, aggregateResult, item, + ); + return CONTINUE_READING_RECORDS; + }, + async () => aggregateResult + ); + + return await doRead(hierarchy, datastore, index, indexedDataKey); +}; + + +const applyItemToAggregateResult = (indexNode, result, item) => { + const getInitialAggregateResult = () => ({ + sum: 0, mean: null, max: null, min: null, + }); + + const applyAggregateResult = (agg, existing, count) => { + const value = compileCode(agg.aggregatedValue)({ record: item }); + + if (!isNumber(value)) return existing; + + existing.sum += value; + existing.max = value > existing.max || existing.max === null + ? value + : existing.max; + existing.min = value < existing.min || existing.min === null + ? value + : existing.min; + existing.mean = existing.sum / count; + return existing; + }; + + for (const aggGroup of indexNode.aggregateGroups) { + if (!has(aggGroup.name)(result)) { + result[aggGroup.name] = {}; + } + + const thisGroupResult = result[aggGroup.name]; + + if (isNonEmptyString(aggGroup.condition)) { + if (!compileExpression(aggGroup.condition)({ record: item })) { + continue; + } + } + + let group = isNonEmptyString(aggGroup.groupBy) + ? compileCode(aggGroup.groupBy)({ record: item }) + : 'all'; + if (!isNonEmptyString(group)) { + group = '(none)'; + } + + if (!has(group)(thisGroupResult)) { + thisGroupResult[group] = { count: 0 }; + for (const agg of aggGroup.aggregates) { + thisGroupResult[group][agg.name] = getInitialAggregateResult(); + } + } + + thisGroupResult[group].count++; + + for (const agg of aggGroup.aggregates) { + const existingValues = thisGroupResult[group][agg.name]; + thisGroupResult[group][agg.name] = applyAggregateResult( + agg, existingValues, + thisGroupResult[group].count, + ); + } + } +}; diff --git a/packages/core/src/indexApi/buildIndex.js b/packages/core/src/indexApi/buildIndex.js new file mode 100644 index 0000000000..eb5534f2b1 --- /dev/null +++ b/packages/core/src/indexApi/buildIndex.js @@ -0,0 +1,198 @@ +import { + find, filter, + includes, some, +} from 'lodash/fp'; +import { getAllIdsIterator } from '../indexing/allIds'; +import { + getFlattenedHierarchy, getRecordNodeById, + getCollectionNodeByKeyOrNodeKey, getNode, isIndex, + isRecord, isDecendant, getAllowedRecordNodesForIndex, + fieldReversesReferenceToIndex, +} from '../templateApi/hierarchy'; +import { + joinKey, apiWrapper, events, $, allTrue, +} from '../common'; +import { + createBuildIndexFolder, + transactionForBuildIndex, +} from '../transactions/create'; +import { permission } from '../authApi/permissions'; +import { BadRequestError } from '../common/errors'; + + +/** rebuilds an index + * @param {object} app - the application container + * @param {string} indexNodeKey - node key of the index, which the index belongs to + */ +export const buildIndex = app => async indexNodeKey => apiWrapper( + app, + events.indexApi.buildIndex, + permission.manageIndex.isAuthorized, + { indexNodeKey }, + _buildIndex, app, indexNodeKey, +); + +const _buildIndex = async (app, indexNodeKey) => { + const indexNode = getNode(app.hierarchy, indexNodeKey); + + await createBuildIndexFolder(app.datastore, indexNodeKey); + + if (!isIndex(indexNode)) { throw new BadRequestError('BuildIndex: must supply an indexnode'); } + + if (indexNode.indexType === 'reference') { + await buildReverseReferenceIndex( + app, indexNode, + ); + } else { + await buildHeirarchalIndex( + app, indexNode, + ); + } + + await app.cleanupTransactions(); +}; + +const buildReverseReferenceIndex = async (app, indexNode) => { + // Iterate through all referencING records, + // and update referenced index for each record + let recordCount = 0; + const referencingNodes = $(app.hierarchy, [ + getFlattenedHierarchy, + filter(n => isRecord(n) + && some(fieldReversesReferenceToIndex(indexNode))(n.fields)), + ]); + + const createTransactionsForReferencingNode = async (referencingNode) => { + const iterateReferencingNodes = await getAllIdsIterator(app)(referencingNode.collectionNodeKey()); + + let referencingIdIterator = await iterateReferencingNodes(); + while (!referencingIdIterator.done) { + const { result } = referencingIdIterator; + for (const id of result.ids) { + const recordKey = joinKey(result.collectionKey, id); + await transactionForBuildIndex(app, indexNode.nodeKey(), recordKey, recordCount); + recordCount++; + } + referencingIdIterator = await iterateReferencingNodes(); + } + }; + + for (const referencingNode of referencingNodes) { + await createTransactionsForReferencingNode(referencingNode); + } +}; + +const getAllowedParentCollectionNodes = (hierarchy, indexNode) => $(getAllowedRecordNodesForIndex(hierarchy, indexNode), [ + map(n => n.parent()), +]); + +const buildHeirarchalIndex = async (app, indexNode) => { + let recordCount = 0; + + const createTransactionsForIds = async (collectionKey, ids) => { + for (const recordId of ids) { + const recordKey = joinKey(collectionKey, recordId); + + const recordNode = getRecordNodeById( + app.hierarchy, + recordId, + ); + + if (recordNodeApplies(indexNode)(recordNode)) { + await transactionForBuildIndex( + app, indexNode.nodeKey(), + recordKey, recordCount, + ); + recordCount++; + } + } + }; + + + const collectionRecords = getAllowedRecordNodesForIndex(app.hierarchy, indexNode); + + for (const targetCollectionRecordNode of collectionRecords) { + const allIdsIterator = await getAllIdsIterator(app)(targetCollectionRecordNode.collectionNodeKey()); + + let allIds = await allIdsIterator(); + while (allIds.done === false) { + await createTransactionsForIds( + allIds.result.collectionKey, + allIds.result.ids, + ); + allIds = await allIdsIterator(); + } + } + + return recordCount; +}; + +const chooseChildRecordNodeByKey = (collectionNode, recordId) => find(c => recordId.startsWith(c.nodeId))(collectionNode.children); + +const recordNodeApplies = indexNode => recordNode => includes(recordNode.nodeId)(indexNode.allowedRecordNodeIds); + +const hasApplicableDecendant = (hierarchy, ancestorNode, indexNode) => $(hierarchy, [ + getFlattenedHierarchy, + filter( + allTrue( + isRecord, + isDecendant(ancestorNode), + recordNodeApplies(indexNode), + ), + ), +]); + +const applyAllDecendantRecords = async (app, collection_Key_or_NodeKey, + indexNode, indexKey, currentIndexedData, + currentIndexedDataKey, recordCount = 0) => { + const collectionNode = getCollectionNodeByKeyOrNodeKey( + app.hierarchy, + collection_Key_or_NodeKey, + ); + + const allIdsIterator = await getAllIdsIterator(app)(collection_Key_or_NodeKey); + + + const createTransactionsForIds = async (collectionKey, allIds) => { + for (const recordId of allIds) { + const recordKey = joinKey(collectionKey, recordId); + + const recordNode = chooseChildRecordNodeByKey( + collectionNode, + recordId, + ); + + if (recordNodeApplies(indexNode)(recordNode)) { + await transactionForBuildIndex( + app, indexNode.nodeKey(), + recordKey, recordCount, + ); + recordCount++; + } + + if (hasApplicableDecendant(app.hierarchy, recordNode, indexNode)) { + for (const childCollectionNode of recordNode.children) { + recordCount = await applyAllDecendantRecords( + app, + joinKey(recordKey, childCollectionNode.collectionName), + indexNode, indexKey, currentIndexedData, + currentIndexedDataKey, recordCount, + ); + } + } + } + }; + + let allIds = await allIdsIterator(); + while (allIds.done === false) { + await createTransactionsForIds( + allIds.result.collectionKey, + allIds.result.ids, + ); + allIds = await allIdsIterator(); + } + + return recordCount; +}; + +export default buildIndex; diff --git a/packages/core/src/indexApi/delete.js b/packages/core/src/indexApi/delete.js new file mode 100644 index 0000000000..e2f8a652e3 --- /dev/null +++ b/packages/core/src/indexApi/delete.js @@ -0,0 +1,43 @@ +import { + tryAwaitOrIgnore, +} from '../common'; +import { + isIndex, isShardedIndex, + getExactNodeForPath, +} from '../templateApi/hierarchy'; +import { + getAllShardKeys, getShardMapKey, + getUnshardedIndexDataKey, +} from '../indexing/sharding'; + +export const _deleteIndex = async (app, indexKey, includeFolder) => { + const indexNode = getExactNodeForPath(app.hierarchy)(indexKey); + + if (!isIndex(indexNode)) { throw new Error('Supplied key is not an index'); } + + if (isShardedIndex(indexNode)) { + const shardKeys = await getAllShardKeys(app, indexKey); + for (const k of shardKeys) { + await tryAwaitOrIgnore( + app.datastore.deleteFile(k), + ); + } + tryAwaitOrIgnore( + await app.datastore.deleteFile( + getShardMapKey(indexKey), + ), + ); + } else { + await tryAwaitOrIgnore( + app.datastore.deleteFile( + getUnshardedIndexDataKey(indexKey), + ), + ); + } + + if (includeFolder) { + tryAwaitOrIgnore( + await app.datastore.deleteFolder(indexKey), + ); + } +}; diff --git a/packages/core/src/indexApi/index.js b/packages/core/src/indexApi/index.js new file mode 100644 index 0000000000..18459f256a --- /dev/null +++ b/packages/core/src/indexApi/index.js @@ -0,0 +1,11 @@ +import { buildIndex } from './buildIndex'; +import { listItems } from './listItems'; +import { aggregates } from './aggregates'; + +export const getIndexApi = app => ({ + listItems: listItems(app), + buildIndex: buildIndex(app), + aggregates: aggregates(app), +}); + +export default getIndexApi; diff --git a/packages/core/src/indexApi/listItems.js b/packages/core/src/indexApi/listItems.js new file mode 100644 index 0000000000..d4b6429799 --- /dev/null +++ b/packages/core/src/indexApi/listItems.js @@ -0,0 +1,66 @@ +import { flatten, merge } from 'lodash/fp'; +import { + safeKey, apiWrapper, $, + events, isNonEmptyString, +} from '../common'; +import { readIndex, searchIndex } from '../indexing/read'; +import { + getUnshardedIndexDataKey, + getShardKeysInRange, +} from '../indexing/sharding'; +import { + getExactNodeForPath, isIndex, + isShardedIndex, +} from '../templateApi/hierarchy'; +import { permission } from '../authApi/permissions'; + +export const listItems = app => async (indexKey, options) => apiWrapper( + app, + events.indexApi.listItems, + permission.readIndex.isAuthorized(indexKey), + { indexKey, options }, + _listItems, app, indexKey, options, +); + +const defaultOptions = { rangeStartParams: null, rangeEndParams: null, searchPhrase: null }; + +const _listItems = async (app, indexKey, options = defaultOptions) => { + const { searchPhrase, rangeStartParams, rangeEndParams } = $({}, [ + merge(options), + merge(defaultOptions), + ]); + + const getItems = async key => (isNonEmptyString(searchPhrase) + ? await searchIndex( + app.hierarchy, + app.datastore, + indexNode, + key, + searchPhrase, + ) + : await readIndex( + app.hierarchy, + app.datastore, + indexNode, + key, + )); + + indexKey = safeKey(indexKey); + const indexNode = getExactNodeForPath(app.hierarchy)(indexKey); + + if (!isIndex(indexNode)) { throw new Error('supplied key is not an index'); } + + if (isShardedIndex(indexNode)) { + const shardKeys = await getShardKeysInRange( + app, indexKey, rangeStartParams, rangeEndParams, + ); + const items = []; + for (const k of shardKeys) { + items.push(await getItems(k)); + } + return flatten(items); + } + return await getItems( + getUnshardedIndexDataKey(indexKey), + ); +}; diff --git a/packages/core/src/indexing/allIds.js b/packages/core/src/indexing/allIds.js new file mode 100644 index 0000000000..08c30c9c7b --- /dev/null +++ b/packages/core/src/indexing/allIds.js @@ -0,0 +1,229 @@ +import { + join, pull, + map, flatten, orderBy, + filter, find, +} from 'lodash/fp'; +import { + getParentKey, + getFlattenedHierarchy, + getCollectionNodeByKeyOrNodeKey, getNodeForCollectionPath, + isCollectionRecord, isAncestor, +} from '../templateApi/hierarchy'; +import { joinKey, safeKey, $ } from '../common'; + +const allIdChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'; + +const allIdsStringsForFactor = (collectionNode) => { + const factor = collectionNode.allidsShardFactor; + const charRangePerShard = 64 / factor; + const allIdStrings = []; + let index = 0; + let currentIdsShard = ''; + while (index < 64) { + currentIdsShard += allIdChars[index]; + if ((index + 1) % charRangePerShard === 0) { + allIdStrings.push(currentIdsShard); + currentIdsShard = ''; + } + index++; + } + + return allIdStrings; +}; + +export const getAllIdsShardNames = (appHierarchy, collectionKey) => { + const collectionRecordNode = getNodeForCollectionPath(appHierarchy)(collectionKey); + return $(collectionRecordNode, [ + c => [c.nodeId], + map(i => map(c => _allIdsShardKey(collectionKey, i, c))(allIdsStringsForFactor(collectionRecordNode))), + flatten, + ]); +}; + +const _allIdsShardKey = (collectionKey, childNo, shardKey) => joinKey( + collectionKey, + 'allids', + childNo, + shardKey, +); + +export const getAllIdsShardKey = (appHierarchy, collectionKey, recordId) => { + const indexOfFirstDash = recordId.indexOf('-'); + + const collectionNode = getNodeForCollectionPath(appHierarchy)(collectionKey); + + const idFirstChar = recordId[indexOfFirstDash + 1]; + const allIdsShardId = $(collectionNode, [ + allIdsStringsForFactor, + find(i => i.includes(idFirstChar)), + ]); + + return _allIdsShardKey( + collectionKey, + recordId.slice(0, indexOfFirstDash), + allIdsShardId, + ); +}; + +const getOrCreateShardFile = async (datastore, allIdsKey) => { + try { + return await datastore.loadFile(allIdsKey); + } catch (eLoad) { + try { + await datastore.createFile(allIdsKey, ''); + return ''; + } catch (eCreate) { + throw new Error( + `Error loading, then creating allIds ${allIdsKey + } : LOAD : ${eLoad.message + } : CREATE : ${eCreate}`, + ); + } + } +}; + +const getShardFile = async (datastore, allIdsKey) => { + try { + return await datastore.loadFile(allIdsKey); + } catch (eLoad) { + return ''; + } +}; + +export const addToAllIds = (appHierarchy, datastore) => async (record) => { + const allIdsKey = getAllIdsShardKey( + appHierarchy, + getParentKey(record.key), + record.id, + ); + + let allIds = await getOrCreateShardFile(datastore, allIdsKey); + + allIds += `${allIds.length > 0 ? ',' : ''}${record.id}`; + + await datastore.updateFile(allIdsKey, allIds); +}; + +export const getAllIdsIterator = app => async (collection_Key_or_NodeKey) => { + collection_Key_or_NodeKey = safeKey(collection_Key_or_NodeKey); + const targetNode = getCollectionNodeByKeyOrNodeKey( + app.hierarchy, + collection_Key_or_NodeKey, + ); + + const getAllIdsIteratorForCollectionKey = async (collectionKey) => { + const all_allIdsKeys = getAllIdsShardNames(app.hierarchy, collectionKey); + let shardIndex = 0; + + const allIdsFromShardIterator = async () => { + if (shardIndex === all_allIdsKeys.length) { return ({ done: true, result: { ids: [], collectionKey } }); } + + const shardKey = all_allIdsKeys[shardIndex]; + + const allIds = await getAllIdsFromShard(app.datastore, shardKey); + + shardIndex++; + + return ({ + result: { + ids: allIds, + collectionKey, + }, + done: false, + }); + }; + + return allIdsFromShardIterator; + }; + + const ancestors = $(getFlattenedHierarchy(app.hierarchy), [ + filter(isCollectionRecord), + filter(n => isAncestor(targetNode)(n) + || n.nodeKey() === targetNode.nodeKey()), + orderBy([n => n.nodeKey().length], ['asc']), + ]); // parents first + + const traverseForIteraterators = async (parentRecordKey = '', currentNodeIndex = 0) => { + const currentNode = ancestors[currentNodeIndex]; + const currentCollectionKey = joinKey( + parentRecordKey, + currentNode.collectionName, + ); + if (currentNode.nodeKey() === targetNode.nodeKey()) { + return [ + await getAllIdsIteratorForCollectionKey( + currentCollectionKey, + )]; + } + const allIterators = []; + const currentIterator = await getAllIdsIteratorForCollectionKey( + currentCollectionKey, + ); + + let ids = await currentIterator(); + while (ids.done === false) { + for (const id of ids.result.ids) { + allIterators.push( + await traverseForIteraterators( + joinKey(currentCollectionKey, id), + currentNodeIndex + 1, + ), + ); + } + + ids = await currentIterator(); + } + + return flatten(allIterators); + }; + + const iteratorsArray = await traverseForIteraterators(); + let currentIteratorIndex = 0; + return async () => { + if (iteratorsArray.length === 0) { return { done: true, result: [] }; } + const innerResult = await iteratorsArray[currentIteratorIndex](); + if (!innerResult.done) { return innerResult; } + if (currentIteratorIndex == iteratorsArray.length - 1) { + return { done: true, result: innerResult.result }; + } + currentIteratorIndex++; + return { done: false, result: innerResult.result }; + }; +}; + +const getAllIdsFromShard = async (datastore, shardKey) => { + const allIdsStr = await getShardFile(datastore, shardKey); + + const allIds = []; + let currentId = ''; + for (let i = 0; i < allIdsStr.length; i++) { + const currentChar = allIdsStr.charAt(i); + const isLast = (i === allIdsStr.length - 1); + if (currentChar === ',' || isLast) { + if (isLast) currentId += currentChar; + allIds.push(currentId); + currentId = ''; + } else { + currentId += currentChar; + } + } + return allIds; +}; + +export const removeFromAllIds = (appHierarchy, datastore) => async (record) => { + const shardKey = getAllIdsShardKey( + appHierarchy, + getParentKey(record.key), + record.id, + ); + const allIds = await getAllIdsFromShard(datastore, shardKey); + + const newIds = $(allIds, [ + pull(record.id), + join(','), + ]); + + await datastore.updateFile(shardKey, newIds); +}; + +export default getAllIdsIterator; diff --git a/packages/core/src/indexing/apply.js b/packages/core/src/indexing/apply.js new file mode 100644 index 0000000000..1626bd6b2f --- /dev/null +++ b/packages/core/src/indexing/apply.js @@ -0,0 +1,79 @@ +import { ensureShardNameIsInShardMap } from './sharding'; +import { getIndexWriter } from './serializer'; +import { isShardedIndex } from '../templateApi/hierarchy'; +import {promiseWriteableStream} from "./promiseWritableStream"; +import {promiseReadableStream} from "./promiseReadableStream"; + +export const applyToShard = async (hierarchy, store, indexKey, + indexNode, indexShardKey, recordsToWrite, keysToRemove) => { + const createIfNotExists = recordsToWrite.length > 0; + const writer = await getWriter(hierarchy, store, indexKey, indexShardKey, indexNode, createIfNotExists); + if (writer === SHARD_DELETED) return; + + await writer.updateIndex(recordsToWrite, keysToRemove); + await swapTempFileIn(store, indexShardKey); +}; + +const SHARD_DELETED = 'SHARD_DELETED'; +const getWriter = async (hierarchy, store, indexKey, indexedDataKey, indexNode, createIfNotExists) => { + let readableStream = null; + + if (isShardedIndex(indexNode)) { + await ensureShardNameIsInShardMap(store, indexKey, indexedDataKey); + if(!await store.exists(indexedDataKey)) { + await store.createFile(indexedDataKey, ""); + } + } + + try { + + readableStream = promiseReadableStream( + await store.readableFileStream(indexedDataKey) + ); + + } catch (e) { + + if (await store.exists(indexedDataKey)) { + throw e; + } else { + if (createIfNotExists) { + await store.createFile(indexedDataKey, ''); + } else { + return SHARD_DELETED; + } + + readableStream = promiseReadableStream( + await store.readableFileStream(indexedDataKey) + ); + + } + } + + const writableStream = promiseWriteableStream( + await store.writableFileStream(indexedDataKey + ".temp") + ); + + return getIndexWriter( + hierarchy, indexNode, + readableStream, writableStream + ); +}; + +const swapTempFileIn = async (store, indexedDataKey, isRetry = false) => { + const tempFile = `${indexedDataKey}.temp`; + try { + await store.deleteFile(indexedDataKey); + } catch (e) { + // ignore failure, incase it has not been created yet + } + try { + await store.renameFile(tempFile, indexedDataKey); + } catch (e) { + // retrying in case delete failure was for some other reason + if (!isRetry) { + await swapTempFileIn(store, indexedDataKey, true); + } else { + throw new Error("Failed to swap in index filed: " + e.message); + } + } +}; diff --git a/packages/core/src/indexing/evaluate.js b/packages/core/src/indexing/evaluate.js new file mode 100644 index 0000000000..a5de760b66 --- /dev/null +++ b/packages/core/src/indexing/evaluate.js @@ -0,0 +1,98 @@ +import { compileExpression, compileCode } from '@nx-js/compiler-util'; +import { + isUndefined, keys, + cloneDeep, isFunction, +} from 'lodash'; +import { defineError } from '../common'; + +export const filterEval = 'FILTER_EVALUATE'; +export const filterCompile = 'FILTER_COMPILE'; +export const mapEval = 'MAP_EVALUATE'; +export const mapCompile = 'MAP_COMPILE'; +export const removeUndeclaredFields = 'REMOVE_UNDECLARED_FIELDS'; +export const addUnMappedFields = 'ADD_UNMAPPED_FIELDS'; +export const addTheKey = 'ADD_KEY'; + + +const getEvaluateResult = () => ({ + isError: false, + passedFilter: true, + result: null, +}); + +export const compileFilter = index => compileExpression(index.filter); + +export const compileMap = index => compileCode(index.map); + +export const passesFilter = (record, index) => { + const context = { record }; + if (!index.filter) return true; + + const compiledFilter = defineError( + () => compileFilter(index), + filterCompile, + ); + + return defineError( + () => compiledFilter(context), + filterEval, + ); +}; + +export const mapRecord = (record, index) => { + const recordClone = cloneDeep(record); + const context = { record: recordClone }; + + const map = index.map ? index.map : 'return {...record};'; + + const compiledMap = defineError( + () => compileCode(map), + mapCompile, + ); + + const mapped = defineError( + () => compiledMap(context), + mapEval, + ); + + const mappedKeys = keys(mapped); + for (let i = 0; i < mappedKeys.length; i++) { + const key = mappedKeys[i]; + mapped[key] = isUndefined(mapped[key]) ? null : mapped[key]; + if (isFunction(mapped[key])) { + delete mapped[key]; + } + } + + mapped.key = record.key; + mapped.sortKey = index.getSortKey + ? compileCode(index.getSortKey)(context) + : record.id; + + return mapped; +}; + +export const evaluate = record => (index) => { + const result = getEvaluateResult(); + + try { + result.passedFilter = passesFilter(record, index); + } catch (err) { + result.isError = true; + result.passedFilter = false; + result.result = err.message; + } + + if (!result.passedFilter) return result; + + try { + result.result = mapRecord(record, index); + } catch (err) { + result.isError = true; + result.result = err.message; + } + + return result; +}; + +export default evaluate; diff --git a/packages/core/src/indexing/indexSchemaCreator.js b/packages/core/src/indexing/indexSchemaCreator.js new file mode 100644 index 0000000000..d658ea50fa --- /dev/null +++ b/packages/core/src/indexing/indexSchemaCreator.js @@ -0,0 +1,58 @@ +import { + has, keys, map, orderBy, + filter, concat, reverse, +} from 'lodash/fp'; +import { getAllowedRecordNodesForIndex } from '../templateApi/hierarchy'; +import { mapRecord } from './evaluate'; +import { constructRecord } from '../recordApi/getNew'; +import { getSampleFieldValue, detectType, all } from '../types'; +import { $ } from '../common'; + +export const generateSchema = (hierarchy, indexNode) => { + const recordNodes = getAllowedRecordNodesForIndex(hierarchy, indexNode); + const mappedRecords = $(recordNodes, [ + map(n => mapRecord(createSampleRecord(n), indexNode)), + ]); + + // always has record key and sort key + const schema = { + sortKey: all.string, + key: all.string, + }; + + const fieldsHas = has(schema); + const setField = (fieldName, value) => { + if (value === null || value === undefined) { return; } + + const thisType = detectType(value); + if (fieldsHas(fieldName)) { + if (schema[fieldName] !== thisType) { + schema[fieldName] = all.string; + } + } else { + schema[fieldName] = thisType; + } + }; + + for (const mappedRec of mappedRecords) { + for (const f in mappedRec) { + setField(f, mappedRec[f]); + } + } + + // returing an array of {name, type} + return $(schema, [ + keys, + map(k => ({ name: k, type: schema[k].name })), + filter(s => s.name !== 'sortKey'), + orderBy('name', ['desc']), // reverse aplha + concat([{ name: 'sortKey', type: all.string.name }]), // sortKey on end + reverse, // sortKey first, then rest are alphabetical + ]); +}; + +const createSampleRecord = recordNode => constructRecord( + recordNode, + getSampleFieldValue, + recordNode.parent().nodeKey(), +); diff --git a/packages/core/src/indexing/initialiseIndex.js b/packages/core/src/indexing/initialiseIndex.js new file mode 100644 index 0000000000..b6a5ad6d29 --- /dev/null +++ b/packages/core/src/indexing/initialiseIndex.js @@ -0,0 +1,22 @@ +import { isShardedIndex } from '../templateApi/hierarchy'; +import { joinKey } from '../common'; +import { getShardMapKey, getUnshardedIndexDataKey, createIndexFile } from './sharding'; + +export const initialiseIndex = async (datastore, parentKey, index) => { + const indexKey = joinKey(parentKey, index.name); + + await datastore.createFolder(indexKey); + + if (isShardedIndex(index)) { + await datastore.createFile( + getShardMapKey(indexKey), + '[]', + ); + } else { + await createIndexFile( + datastore, + getUnshardedIndexDataKey(indexKey), + index, + ); + } +}; diff --git a/packages/core/src/indexing/promiseReadableStream.js b/packages/core/src/indexing/promiseReadableStream.js new file mode 100644 index 0000000000..cefb0bf3d6 --- /dev/null +++ b/packages/core/src/indexing/promiseReadableStream.js @@ -0,0 +1,84 @@ +// adapted from https://github.com/dex4er/js-promise-readable +// thanks :) + +export const promiseReadableStream = stream => { + + let _errored; + + const _errorHandler = err => { + _errored = err; + }; + + stream.on("error", _errorHandler); + + const read = (size) => { + + return new Promise((resolve, reject) => { + if (_errored) { + const err = _errored; + _errored = undefined; + return reject(err) + } + + if (!stream.readable || stream.closed || stream.destroyed) { + return resolve(); + } + + const readableHandler = () => { + const chunk = stream.read(size); + + if (chunk) { + removeListeners(); + resolve(chunk); + } + } + + const closeHandler = () => { + removeListeners(); + resolve(); + } + + const endHandler = () => { + removeListeners(); + resolve(); + } + + const errorHandler = (err) => { + _errored = undefined; + removeListeners(); + reject(err); + } + + const removeListeners = () => { + stream.removeListener("close", closeHandler); + stream.removeListener("error", errorHandler); + stream.removeListener("end", endHandler); + stream.removeListener("readable", readableHandler); + } + + stream.on("close", closeHandler); + stream.on("end", endHandler); + stream.on("error", errorHandler); + stream.on("readable", readableHandler); + + readableHandler(); + }); + } + + + const destroy = () => { + if (stream) { + if (_errorHandler) { + stream.removeListener("error", _errorHandler); + } + if (typeof stream.destroy === "function") { + stream.destroy(); + } + } + }; + + return {read, destroy, stream}; + } + + export default promiseReadableStream + \ No newline at end of file diff --git a/packages/core/src/indexing/promiseWritableStream.js b/packages/core/src/indexing/promiseWritableStream.js new file mode 100644 index 0000000000..e5f07b92be --- /dev/null +++ b/packages/core/src/indexing/promiseWritableStream.js @@ -0,0 +1,120 @@ + // adapted from https://github.com/dex4er/js-promise-writable + // Thank you :) + export const promiseWriteableStream = stream => { + + let _errored; + + const _errorHandler = err => { + _errored = err; + }; + + stream.on("error", _errorHandler); + + const write = chunk => { + let rejected = false; + + return new Promise((resolve, reject) => { + if (_errored) { + const err = _errored; + _errored = undefined; + return reject(err); + } + + if (!stream.writable || stream.closed || stream.destroyed) { + return reject(new Error("write after end")); + } + + const writeErrorHandler = err => { + _errored = undefined; + rejected = true; + reject(err); + } + + stream.once("error", writeErrorHandler); + + const canWrite = stream.write(chunk); + + stream.removeListener("error", writeErrorHandler); + + if (canWrite) { + if (!rejected) { + resolve(chunk.length); + } + } else { + const errorHandler = err => { + _errored = undefined; + removeListeners(); + reject(err); + } + + const drainHandler = () => { + removeListeners(); + resolve(chunk.length); + } + + const closeHandler = () => { + removeListeners(); + resolve(chunk.length); + } + + const finishHandler = () => { + removeListeners(); + resolve(chunk.length); + } + + const removeListeners = () => { + stream.removeListener("close", closeHandler); + stream.removeListener("drain", drainHandler); + stream.removeListener("error", errorHandler); + stream.removeListener("finish", finishHandler); + } + + stream.on("close", closeHandler); + stream.on("drain", drainHandler); + stream.on("error", errorHandler); + stream.on("finish", finishHandler); + } + }) + } + + const end = () => { + + return new Promise((resolve, reject) => { + if (_errored) { + const err = _errored; + _errored = undefined; + return reject(err); + } + + if (!stream.writable || stream.closed || stream.destroyed) { + return resolve(); + } + + const finishHandler = () => { + removeListeners(); + resolve(); + } + + const errorHandler = (err) => { + _errored = undefined; + removeListeners(); + reject(err); + } + + const removeListeners = () => { + stream.removeListener("error", errorHandler); + stream.removeListener("finish", finishHandler); + } + + stream.on("finish", finishHandler); + stream.on("error", errorHandler); + + stream.end(); + }) + } + + return {write, end}; + } + + export default promiseWriteableStream + \ No newline at end of file diff --git a/packages/core/src/indexing/read.js b/packages/core/src/indexing/read.js new file mode 100644 index 0000000000..ffb2c5da8e --- /dev/null +++ b/packages/core/src/indexing/read.js @@ -0,0 +1,99 @@ +import lunr from 'lunr'; +import { + getHashCode, + joinKey +} from '../common'; +import { + getActualKeyOfParent, + isGlobalIndex, +} from '../templateApi/hierarchy'; +import {promiseReadableStream} from "./promiseReadableStream"; +import { createIndexFile } from './sharding'; +import { generateSchema } from './indexSchemaCreator'; +import { getIndexReader, CONTINUE_READING_RECORDS } from './serializer'; + +export const readIndex = async (hierarchy, datastore, index, indexedDataKey) => { + const records = []; + const doRead = iterateIndex( + async item => { + records.push(item); + return CONTINUE_READING_RECORDS; + }, + async () => records + ); + + return await doRead(hierarchy, datastore, index, indexedDataKey); +}; + +export const searchIndex = async (hierarchy, datastore, index, indexedDataKey, searchPhrase) => { + const records = []; + const schema = generateSchema(hierarchy, index); + const doRead = iterateIndex( + async item => { + const idx = lunr(function () { + this.ref('key'); + for (const field of schema) { + this.field(field.name); + } + this.add(item); + }); + const searchResults = idx.search(searchPhrase); + if (searchResults.length === 1) { + item._searchResult = searchResults[0]; + records.push(item); + } + return CONTINUE_READING_RECORDS; + }, + async () => records + ); + + return await doRead(hierarchy, datastore, index, indexedDataKey); +}; + +export const getIndexedDataKey_fromIndexKey = (indexKey) => + `${indexKey}${indexKey.endsWith('.csv') ? '' : '.csv'}`; + +export const uniqueIndexName = index => `idx_${ + getHashCode(`${index.filter}${index.map}`) +}.csv`; + +export const getIndexedDataKey = (decendantKey, indexNode) => { + if (isGlobalIndex(indexNode)) { return `${indexNode.nodeKey()}.csv`; } + + const indexedDataParentKey = getActualKeyOfParent( + indexNode.parent().nodeKey(), + decendantKey, + ); + + const indexName = indexNode.name + ? `${indexNode.name}.csv` + : uniqueIndexName(indexNode); + + return joinKey( + indexedDataParentKey, + indexName, + ); +}; + +export const iterateIndex = (onGetItem, getFinalResult) => async (hierarchy, datastore, index, indexedDataKey) => { + try { + const readableStream = promiseReadableStream( + await datastore.readableFileStream(indexedDataKey) + ); + + const read = getIndexReader(hierarchy, index, readableStream); + await read(onGetItem); + return getFinalResult(); + } catch (e) { + if (await datastore.exists(indexedDataKey)) { + throw e; + } else { + await createIndexFile( + datastore, + indexedDataKey, + index, + ); + } + return []; + } +}; diff --git a/packages/core/src/indexing/relevant.js b/packages/core/src/indexing/relevant.js new file mode 100644 index 0000000000..58f03493c0 --- /dev/null +++ b/packages/core/src/indexing/relevant.js @@ -0,0 +1,81 @@ +import { orderBy } from 'lodash'; +import { + reduce, find, includes, flatten, union, + filter, each, map, +} from 'lodash/fp'; +import { + joinKey, splitKey, isNonEmptyString, + isNothing, $, isSomething, +} from '../common'; +import { + getFlattenedHierarchy, getNode, getRecordNodeId, + getExactNodeForPath, recordNodeIdIsAllowed, + isRecord, isGlobalIndex, +} from '../templateApi/hierarchy'; +import { indexTypes } from '../templateApi/indexes'; + +export const getRelevantAncestorIndexes = (appHierarchy, record) => { + const key = record.key; + const keyParts = splitKey(key); + const nodeId = getRecordNodeId(key); + + const flatHierarchy = orderBy(getFlattenedHierarchy(appHierarchy), + [node => node.pathRegx().length], + ['desc']); + + const makeindexNodeAndKey_ForAncestorIndex = (indexNode, indexKey) => makeIndexNodeAndKey(indexNode, joinKey(indexKey, indexNode.name)); + + const traverseAncestorIndexesInPath = () => reduce((acc, part) => { + const currentIndexKey = joinKey(acc.lastIndexKey, part); + acc.lastIndexKey = currentIndexKey; + const testPathRegx = p => new RegExp(`${p.pathRegx()}$`).test(currentIndexKey); + const nodeMatch = find(testPathRegx)(flatHierarchy); + + if (isNothing(nodeMatch)) { return acc; } + + if (!isRecord(nodeMatch) + || nodeMatch.indexes.length === 0) { return acc; } + + const indexes = $(nodeMatch.indexes, [ + filter(i => i.indexType === indexTypes.ancestor + && (i.allowedRecordNodeIds.length === 0 + || includes(nodeId)(i.allowedRecordNodeIds))), + ]); + + each(v => acc.nodesAndKeys.push( + makeindexNodeAndKey_ForAncestorIndex(v, currentIndexKey), + ))(indexes); + + return acc; + }, { lastIndexKey: '', nodesAndKeys: [] })(keyParts).nodesAndKeys; + + const rootIndexes = $(flatHierarchy, [ + filter(n => isGlobalIndex(n) && recordNodeIdIsAllowed(n)(nodeId)), + map(i => makeIndexNodeAndKey(i, i.nodeKey())), + ]); + + return union(traverseAncestorIndexesInPath())(rootIndexes); +}; + +export const getRelevantReverseReferenceIndexes = (appHierarchy, record) => $(record.key, [ + getExactNodeForPath(appHierarchy), + n => n.fields, + filter(f => f.type === 'reference' + && isSomething(record[f.name]) + && isNonEmptyString(record[f.name].key)), + map(f => $(f.typeOptions.reverseIndexNodeKeys, [ + map(n => ({ + recordNode: getNode(appHierarchy, n), + field: f, + })), + ])), + flatten, + map(n => makeIndexNodeAndKey( + n.recordNode, + joinKey(record[n.field.name].key, n.recordNode.name), + )), +]); + +const makeIndexNodeAndKey = (indexNode, indexKey) => ({ indexNode, indexKey }); + +export default getRelevantAncestorIndexes; diff --git a/packages/core/src/indexing/serializer.js b/packages/core/src/indexing/serializer.js new file mode 100644 index 0000000000..c01f7e5335 --- /dev/null +++ b/packages/core/src/indexing/serializer.js @@ -0,0 +1,252 @@ +import {generateSchema} from "./indexSchemaCreator"; +import { has, isString, difference, find } from "lodash/fp"; +import { Buffer } from "safe-buffer"; +import {StringDecoder} from "string_decoder"; +import {getType} from "../types"; +import { isSomething } from "../common"; + +export const BUFFER_MAX_BYTES = 524288; // 0.5Mb + +export const CONTINUE_READING_RECORDS = "CONTINUE_READING"; +export const READ_REMAINING_TEXT = "READ_REMAINING"; +export const CANCEL_READ = "CANCEL"; + +export const getIndexWriter = (hierarchy, indexNode, readableStream, writableStream, end) => { + const schema = generateSchema(hierarchy, indexNode); + + return ({ + read: read(readableStream, schema), + updateIndex: updateIndex(readableStream, writableStream, schema, end) + }); +}; + +export const getIndexReader = (hierarchy, indexNode, readableStream) => + read( + readableStream, + generateSchema(hierarchy, indexNode) + ); + +const updateIndex = (readableStream, writableStream, schema) => async (itemsToWrite, keysToRemove) => { + const write = newOutputWriter(BUFFER_MAX_BYTES, writableStream); + const writtenItems = []; + await read(readableStream, schema)( + async indexedItem => { + const updated = find(i => indexedItem.key === i.key)(itemsToWrite); + const removed = find(k => indexedItem.key === k)(keysToRemove); + + if(isSomething(removed)) + return CONTINUE_READING_RECORDS; + + if(isSomething(updated)) { + const serializedItem = serializeItem(schema, updated); + await write(serializedItem); + writtenItems.push(updated); + } else { + await write( + serializeItem(schema, indexedItem) + ); + } + + return CONTINUE_READING_RECORDS; + + }, + async text => await write(text) + ); + + if(writtenItems.length !== itemsToWrite.length) { + const toAdd = difference(itemsToWrite, writtenItems); + for(let added of toAdd) { + await write( + serializeItem(schema, added) + ); + } + } else if(writtenItems.length === 0) { + // potentially are no records + await write(""); + } + + await write(); + await writableStream.end(); +}; + +const read = (readableStream, schema) => async (onGetItem, onGetText) => { + const readInput = newInputReader(readableStream); + let text = await readInput(); + let status = CONTINUE_READING_RECORDS; + while(text.length > 0) { + + if(status === READ_REMAINING_TEXT) { + await onGetText(text); + continue; + } + + if(status === CANCEL_READ) { + return; + } + + let rowText = ""; + let currentCharIndex=0; + for(let currentChar of text) { + rowText += currentChar; + if(currentChar === "\r") { + status = await onGetItem( + deserializeRow(schema, rowText) + ); + rowText = ""; + if(status === READ_REMAINING_TEXT) { + break; + } + } + currentCharIndex++; + } + + if(currentCharIndex < text.length -1) { + await onGetText(text.substring(currentCharIndex + 1)); + } + + text = await readInput(); + } + + await readableStream.destroy(); + +}; + +const newOutputWriter = (flushBoundary, writableStream) => { + + let currentBuffer = null; + + return async (text) => { + + if(isString(text) && currentBuffer === null) + currentBuffer = Buffer.from(text, "utf8"); + else if(isString(text)) + currentBuffer = Buffer.concat([ + currentBuffer, + Buffer.from(text, "utf8") + ]); + + if(currentBuffer !== null && + (currentBuffer.length > flushBoundary + || !isString(text))) { + + await writableStream.write(currentBuffer); + currentBuffer = null; + } + } +}; + +const newInputReader = (readableStream) => { + + const decoder = new StringDecoder('utf8'); + let remainingBytes = []; + + return async () => { + + let nextBytesBuffer = await readableStream.read(BUFFER_MAX_BYTES); + const remainingBuffer = Buffer.from(remainingBytes); + + if(!nextBytesBuffer) nextBytesBuffer = Buffer.from([]); + + const moreToRead = nextBytesBuffer.length === BUFFER_MAX_BYTES; + + const buffer = Buffer.concat( + [remainingBuffer, nextBytesBuffer], + remainingBuffer.length + nextBytesBuffer.length); + + const text = decoder.write(buffer); + remainingBytes = decoder.end(buffer); + + if(!moreToRead && remainingBytes.length > 0) { + // if for any reason, we have remaining bytes at the end + // of the stream, just discard - dont see why this should + // ever happen, but if it does, it could cause a stack overflow + remainingBytes = []; + } + + return text; + }; +}; + +const deserializeRow = (schema, rowText) => { + let currentPropIndex = 0; + let currentCharIndex = 0; + let currentValueText = ""; + let isEscaped = false; + const item = {}; + + const setCurrentProp = () => { + const currentProp = schema[currentPropIndex]; + const type = getType(currentProp.type); + const value = currentValueText === "" + ? type.getDefaultValue() + : type.safeParseValue( + currentValueText); + item[currentProp.name] = value; + }; + + while(currentPropIndex < schema.length) { + + if(currentCharIndex < rowText.length) { + const currentChar = rowText[currentCharIndex]; + if(isEscaped) { + if(currentChar === "r") { + currentValueText += "\r"; + } else { + currentValueText += currentChar; + } + isEscaped = false; + } else { + if(currentChar === ",") { + setCurrentProp(); + currentValueText = ""; + currentPropIndex++; + } else if(currentChar === "\\") { + isEscaped = true; + } else { + currentValueText += currentChar; + } + } + currentCharIndex++; + } else { + currentValueText = ""; + setCurrentProp(); + currentPropIndex++; + } + } + + return item; +}; + +export const serializeItem = (schema, item) => { + + let rowText = "" + + for(let prop of schema) { + const type = getType(prop.type); + const value = has(prop.name)(item) + ? item[prop.name] + : type.getDefaultValue() + + const valStr = type.stringify(value); + + for(let i = 0; i < valStr.length; i++) { + const currentChar = valStr[i]; + if(currentChar === "," + || currentChar === "\r" + || currentChar === "\\") { + rowText += "\\"; + } + + if(currentChar === "\r") { + rowText += "r"; + } else { + rowText += currentChar; + } + } + + rowText += ","; + } + + rowText += "\r"; + return rowText; +}; \ No newline at end of file diff --git a/packages/core/src/indexing/sharding.js b/packages/core/src/indexing/sharding.js new file mode 100644 index 0000000000..57a73d2b8c --- /dev/null +++ b/packages/core/src/indexing/sharding.js @@ -0,0 +1,124 @@ +import { compileCode } from '@nx-js/compiler-util'; +import { + filter, includes, map, last, +} from 'lodash/fp'; +import { + getActualKeyOfParent, isGlobalIndex, + getParentKey, isShardedIndex, + getExactNodeForPath, +} from '../templateApi/hierarchy'; +import { + joinKey, isNonEmptyString, splitKey, $, +} from '../common'; + +export const getIndexedDataKey = (indexNode, indexKey, record) => { + const getShardName = (indexNode, record) => { + const shardNameFunc = compileCode(indexNode.getShardName); + try { + return shardNameFunc({ record }); + } catch(e) { + const errorDetails = `shardCode: ${indexNode.getShardName} :: record: ${JSON.stringify(record)} :: ` + e.message = "Error running index shardname func: " + errorDetails + e.message; + throw e; + } + }; + + const shardName = isNonEmptyString(indexNode.getShardName) + ? `${getShardName(indexNode, record)}.csv` + : 'index.csv'; + + return joinKey(indexKey, shardName); +}; + +export const getShardKeysInRange = async (app, indexKey, startRecord = null, endRecord = null) => { + const indexNode = getExactNodeForPath(app.hierarchy)(indexKey); + + const startShardName = !startRecord + ? null + : shardNameFromKey( + getIndexedDataKey( + indexNode, + indexKey, + startRecord, + ), + ); + + const endShardName = !endRecord + ? null + : shardNameFromKey( + getIndexedDataKey( + indexNode, + indexKey, + endRecord, + ), + ); + + return $(await getShardMap(app.datastore, indexKey), [ + filter(k => (startRecord === null || k >= startShardName) + && (endRecord === null || k <= endShardName)), + map(k => joinKey(indexKey, `${k}.csv`)), + ]); +}; + +export const ensureShardNameIsInShardMap = async (store, indexKey, indexedDataKey) => { + const map = await getShardMap(store, indexKey); + const shardName = shardNameFromKey(indexedDataKey); + if (!includes(shardName)(map)) { + map.push(shardName); + await writeShardMap(store, indexKey, map); + } +}; + +export const getShardMap = async (datastore, indexKey) => { + const shardMapKey = getShardMapKey(indexKey); + try { + return await datastore.loadJson(shardMapKey); + } catch (_) { + await datastore.createJson(shardMapKey, []); + return []; + } +}; + +export const writeShardMap = async (datastore, indexKey, shardMap) => await datastore.updateJson( + getShardMapKey(indexKey), + shardMap, +); + +export const getAllShardKeys = async (app, indexKey) => await getShardKeysInRange(app, indexKey); + +export const getShardMapKey = indexKey => joinKey(indexKey, 'shardMap.json'); + +export const getUnshardedIndexDataKey = indexKey => joinKey(indexKey, 'index.csv'); + +export const getIndexFolderKey = indexKey => indexKey; + +export const createIndexFile = async (datastore, indexedDataKey, index) => { + if (isShardedIndex(index)) { + const indexKey = getParentKey(indexedDataKey); + const shardMap = await getShardMap(datastore, indexKey); + shardMap.push( + shardNameFromKey(indexedDataKey), + ); + await writeShardMap(datastore, indexKey, shardMap); + } + await datastore.createFile(indexedDataKey, ''); +}; + +export const shardNameFromKey = key => $(key, [ + splitKey, + last, +]).replace('.csv', ''); + +export const getIndexKey_BasedOnDecendant = (decendantKey, indexNode) => { + if (isGlobalIndex(indexNode)) { return `${indexNode.nodeKey()}`; } + + const indexedDataParentKey = getActualKeyOfParent( + indexNode.parent().nodeKey(), + decendantKey, + ); + + return joinKey( + indexedDataParentKey, + indexNode.name, + ); +}; diff --git a/packages/core/src/indexing/sweeper.js b/packages/core/src/indexing/sweeper.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/recordApi/customId.js b/packages/core/src/recordApi/customId.js new file mode 100644 index 0000000000..7ceb6f9dd2 --- /dev/null +++ b/packages/core/src/recordApi/customId.js @@ -0,0 +1,29 @@ +import { find, take, union } from 'lodash/fp'; +import { getFlattenedHierarchy } from '../templateApi/hierarchy'; +import { $, splitKey, joinKey } from '../common'; +import { NotFoundError } from '../common/errors'; + +export const customId = app => (nodeName, id) => { + const node = $(app.hierarchy, [ + getFlattenedHierarchy, + find(n => n.name === nodeName), + ]); + + if (!node) throw new NotFoundError(`Cannot find node ${nodeName}`); + + return `${node.nodeId}-${id}`; +}; + +export const setCustomId = app => (record, id) => { + record.id = customId(app)(record.type, id); + + const keyParts = splitKey(record.key); + + record.key = $(keyParts, [ + take(keyParts.length - 1), + union([record.id]), + joinKey, + ]); + + return record; +}; diff --git a/packages/core/src/recordApi/delete.js b/packages/core/src/recordApi/delete.js new file mode 100644 index 0000000000..50449866c3 --- /dev/null +++ b/packages/core/src/recordApi/delete.js @@ -0,0 +1,96 @@ +import { + safeKey, apiWrapper, + events, joinKey, +} from '../common'; +import { _load, getRecordFileName } from './load'; +import { _deleteCollection } from '../collectionApi/delete'; +import { + getExactNodeForPath, + getFlattenedHierarchy, getNode, + fieldReversesReferenceToNode, +} from '../templateApi/hierarchy'; +import { _deleteIndex } from '../indexApi/delete'; +import { transactionForDeleteRecord } from '../transactions/create'; +import { removeFromAllIds } from '../indexing/allIds'; +import { permission } from '../authApi/permissions'; + +export const deleteRecord = (app, disableCleanup = false) => async key => apiWrapper( + app, + events.recordApi.delete, + permission.deleteRecord.isAuthorized(key), + { key }, + _deleteRecord, app, key, disableCleanup, +); + +// called deleteRecord because delete is a keyword +export const _deleteRecord = async (app, key, disableCleanup) => { + key = safeKey(key); + const node = getExactNodeForPath(app.hierarchy)(key); + + const record = await _load(app, key); + await transactionForDeleteRecord(app, record); + + for (const collectionRecord of node.children) { + const collectionKey = joinKey( + key, collectionRecord.collectionName, + ); + await _deleteCollection(app, collectionKey, true); + } + + await app.datastore.deleteFile( + getRecordFileName(key), + ); + + await deleteFiles(app, key); + + await removeFromAllIds(app.hierarchy, app.datastore)(record); + + if (!disableCleanup) { await app.cleanupTransactions(); } + + await app.datastore.deleteFolder(key); + await deleteIndexes(app, key); +}; + +const deleteIndexes = async (app, key) => { + const node = getExactNodeForPath(app.hierarchy)(key); + /* const reverseIndexKeys = $(app.hierarchy, [ + getFlattenedHierarchy, + map(n => n.fields), + flatten, + filter(isSomething), + filter(fieldReversesReferenceToNode(node)), + map(f => $(f.typeOptions.reverseIndexNodeKeys, [ + map(n => getNode( + app.hierarchy, + n)) + ]) + ), + flatten, + map(n => joinKey(key, n.name)) + ]); + + for(let i of reverseIndexKeys) { + await _deleteIndex(app, i, true); + } */ + + + for (const index of node.indexes) { + const indexKey = joinKey(key, index.name); + await _deleteIndex(app, indexKey, true); + } +}; + +const deleteFiles = async (app, key) => { + const filesFolder = joinKey(key, 'files'); + const allFiles = await app.datastore.getFolderContents( + filesFolder, + ); + + for (const file of allFiles) { + await app.datastore.deleteFile(file); + } + + await app.datastore.deleteFolder( + joinKey(key, 'files'), + ); +}; diff --git a/packages/core/src/recordApi/downloadFile.js b/packages/core/src/recordApi/downloadFile.js new file mode 100644 index 0000000000..d032097cca --- /dev/null +++ b/packages/core/src/recordApi/downloadFile.js @@ -0,0 +1,24 @@ +import { apiWrapper, events, isNothing } from '../common'; +import { permission } from '../authApi/permissions'; +import { safeGetFullFilePath } from './uploadFile'; +import { BadRequestError } from '../common/errors'; + +export const downloadFile = app => async (recordKey, relativePath) => apiWrapper( + app, + events.recordApi.uploadFile, + permission.readRecord.isAuthorized(recordKey), + { recordKey, relativePath },//remove dupe key 'recordKey' from object + _downloadFile, app, recordKey, relativePath, +); + + +const _downloadFile = async (app, recordKey, relativePath) => { + if (isNothing(recordKey)) { throw new BadRequestError('Record Key not supplied'); } + if (isNothing(relativePath)) { throw new BadRequestError('file path not supplied'); } + + return await app.datastore.readableFileStream( + safeGetFullFilePath( + recordKey, relativePath, + ), + ); +}; diff --git a/packages/core/src/recordApi/getContext.js b/packages/core/src/recordApi/getContext.js new file mode 100644 index 0000000000..3c36632400 --- /dev/null +++ b/packages/core/src/recordApi/getContext.js @@ -0,0 +1,75 @@ +import { has, some } from 'lodash'; +import { map, isString } from 'lodash/fp'; +import { + getExactNodeForPath, + findField, getNode, isGlobalIndex, +} from '../templateApi/hierarchy'; +import { listItems } from '../indexApi/listItems'; +import { + $, apiWrapperSync, events, +} from '../common'; +import { getIndexKey_BasedOnDecendant } from '../indexing/sharding'; +import { permission } from '../authApi/permissions'; + +export const getContext = app => recordKey => apiWrapperSync( + app, + events.recordApi.getContext, + permission.readRecord.isAuthorized(recordKey), + { recordKey }, + _getContext, app, recordKey, +); + +export const _getContext = (app, recordKey) => { + const recordNode = getExactNodeForPath(app.hierarchy)(recordKey); + + const cachedReferenceIndexes = {}; + + const lazyLoadReferenceIndex = async (typeOptions) => { + if (!has(cachedReferenceIndexes, typeOptions.indexNodeKey)) { + cachedReferenceIndexes[typeOptions.indexNodeKey] = { + typeOptions, + data: await readReferenceIndex( + app, recordKey, typeOptions, + ), + }; + } + + return cachedReferenceIndexes[typeOptions.indexNodeKey]; + }; + + const getTypeOptions = typeOptions_or_fieldName => (isString(typeOptions_or_fieldName) + ? findField(recordNode, typeOptions_or_fieldName) + .typeOptions + : typeOptions_or_fieldName); + + return { + referenceExists: async (typeOptions_or_fieldName, key) => { + const typeOptions = getTypeOptions(typeOptions_or_fieldName); + const { data } = await lazyLoadReferenceIndex(typeOptions); + return some(data, i => i.key === key); + }, + referenceOptions: async (typeOptions_or_fieldName) => { + const typeOptions = getTypeOptions(typeOptions_or_fieldName); + const { data } = await lazyLoadReferenceIndex(typeOptions); + return data; + }, + recordNode, + }; +}; + +const readReferenceIndex = async (app, recordKey, typeOptions) => { + const indexNode = getNode(app.hierarchy, typeOptions.indexNodeKey); + const indexKey = isGlobalIndex(indexNode) + ? indexNode.nodeKey() + : getIndexKey_BasedOnDecendant( + recordKey, indexNode, + ); + + const items = await listItems(app)(indexKey); + return $(items, [ + map(i => ({ + key: i.key, + value: i[typeOptions.displayValue], + })), + ]); +}; diff --git a/packages/core/src/recordApi/getNew.js b/packages/core/src/recordApi/getNew.js new file mode 100644 index 0000000000..9c53bd9aa5 --- /dev/null +++ b/packages/core/src/recordApi/getNew.js @@ -0,0 +1,44 @@ +import { + keyBy, mapValues, +} from 'lodash/fp'; +import { generate } from 'shortid'; +import { getNodeForCollectionPath } from '../templateApi/hierarchy'; +import { getNewFieldValue } from '../types'; +import { + $, joinKey, safeKey, apiWrapperSync, events, +} from '../common'; +import { permission } from '../authApi/permissions'; + +export const getNew = app => (collectionKey, recordTypeName) => { + const recordNode = getRecordNode(app, collectionKey, recordTypeName); + return apiWrapperSync( + app, + events.recordApi.getNew, + permission.createRecord.isAuthorized(recordNode.nodeKey()), + { collectionKey, recordTypeName }, + _getNew, recordNode, collectionKey, + ); +}; + +const _getNew = (recordNode, collectionKey) => constructRecord(recordNode, getNewFieldValue, collectionKey); + +const getRecordNode = (app, collectionKey) => { + collectionKey = safeKey(collectionKey); + return getNodeForCollectionPath(app.hierarchy)(collectionKey); +}; + +export const getNewChild = app => (recordKey, collectionName, recordTypeName) => + getNew(app)(joinKey(recordKey, collectionName), recordTypeName); + +export const constructRecord = (recordNode, getFieldValue, collectionKey) => { + const record = $(recordNode.fields, [ + keyBy('name'), + mapValues(getFieldValue), + ]); + + record.id = `${recordNode.nodeId}-${generate()}`; + record.key = joinKey(collectionKey, record.id); + record.isNew = true; + record.type = recordNode.name; + return record; +}; diff --git a/packages/core/src/recordApi/index.js b/packages/core/src/recordApi/index.js new file mode 100644 index 0000000000..c9d5998bd8 --- /dev/null +++ b/packages/core/src/recordApi/index.js @@ -0,0 +1,28 @@ +import { getNew, getNewChild } from './getNew'; +import { load } from './load'; +import { validate } from './validate'; +import { getContext } from './getContext'; +import { save } from './save'; +import { deleteRecord } from './delete'; +import { uploadFile } from './uploadFile'; +import { downloadFile } from './downloadFile'; +import { customId, setCustomId } from './customId'; + +const api = app => ({ + getNew: getNew(app), + getNewChild: getNewChild(app), + save: save(app), + load: load(app), + delete: deleteRecord(app, false), + validate: validate(app), + getContext: getContext(app), + uploadFile: uploadFile(app), + downloadFile: downloadFile(app), + customId: customId(app), + setCustomId: setCustomId(app), +}); + + +export const getRecordApi = app => api(app); + +export default getRecordApi; diff --git a/packages/core/src/recordApi/load.js b/packages/core/src/recordApi/load.js new file mode 100644 index 0000000000..37cf74debf --- /dev/null +++ b/packages/core/src/recordApi/load.js @@ -0,0 +1,70 @@ +import { + keyBy, mapValues, filter, + map, includes, last, +} from 'lodash/fp'; +import { getExactNodeForPath, getNode } from '../templateApi/hierarchy'; +import { safeParseField } from '../types'; +import { + $, splitKey, safeKey, isNonEmptyString, + apiWrapper, events, joinKey, +} from '../common'; +import { mapRecord } from '../indexing/evaluate'; +import { permission } from '../authApi/permissions'; + +export const getRecordFileName = key => joinKey(key, 'record.json'); + +export const load = app => async key => apiWrapper( + app, + events.recordApi.load, + permission.readRecord.isAuthorized(key), + { key }, + _load, app, key, +); + +export const _load = async (app, key, keyStack = []) => { + key = safeKey(key); + const recordNode = getExactNodeForPath(app.hierarchy)(key); + const storedData = await app.datastore.loadJson( + getRecordFileName(key), + ); + + const loadedRecord = $(recordNode.fields, [ + keyBy('name'), + mapValues(f => safeParseField(f, storedData)), + ]); + + const newKeyStack = [...keyStack, key]; + + const references = $(recordNode.fields, [ + filter(f => f.type === 'reference' + && isNonEmptyString(loadedRecord[f.name].key) + && !includes(loadedRecord[f.name].key)(newKeyStack)), + map(f => ({ + promise: _load(app, loadedRecord[f.name].key, newKeyStack), + index: getNode(app.hierarchy, f.typeOptions.indexNodeKey), + field: f, + })), + ]); + + if (references.length > 0) { + const refRecords = await Promise.all( + map(p => p.promise)(references), + ); + + for (const ref of references) { + loadedRecord[ref.field.name] = mapRecord( + refRecords[references.indexOf(ref)], + ref.index, + ); + } + } + + loadedRecord.transactionId = storedData.transactionId; + loadedRecord.isNew = false; + loadedRecord.key = key; + loadedRecord.id = $(key, [splitKey, last]); + loadedRecord.type = recordNode.name; + return loadedRecord; +}; + +export default load; diff --git a/packages/core/src/recordApi/save.js b/packages/core/src/recordApi/save.js new file mode 100644 index 0000000000..aef38476f2 --- /dev/null +++ b/packages/core/src/recordApi/save.js @@ -0,0 +1,184 @@ +import { + cloneDeep, + flatten, + map, + filter, +} from 'lodash/fp'; +import { initialiseChildCollections } from '../collectionApi/initialise'; +import { validate } from './validate'; +import { _load, getRecordFileName } from './load'; +import { + apiWrapper, events, $, joinKey, +} from '../common'; +import { + getFlattenedHierarchy, + getExactNodeForPath, + isRecord, + getNode, + fieldReversesReferenceToNode, +} from '../templateApi/hierarchy'; +import { mapRecord } from '../indexing/evaluate'; +import { listItems } from '../indexApi/listItems'; +import { addToAllIds } from '../indexing/allIds'; +import { + transactionForCreateRecord, + transactionForUpdateRecord, +} from '../transactions/create'; +import { permission } from '../authApi/permissions'; +import { initialiseIndex } from '../indexing/initialiseIndex'; +import { BadRequestError } from '../common/errors'; + +export const save = app => async (record, context) => apiWrapper( + app, + events.recordApi.save, + record.isNew + ? permission.createRecord.isAuthorized(record.key) + : permission.updateRecord.isAuthorized(record.key), { record }, + _save, app, record, context, false, +); + + +export const _save = async (app, record, context, skipValidation = false) => { + const recordClone = cloneDeep(record); + if (!skipValidation) { + const validationResult = await validate(app)(recordClone, context); + if (!validationResult.isValid) { + await app.publish(events.recordApi.save.onInvalid, { record, validationResult }); + throw new BadRequestError(`Save : Record Invalid : ${ + JSON.stringify(validationResult.errors)}`); + } + } + + if (recordClone.isNew) { + await addToAllIds(app.hierarchy, app.datastore)(recordClone); + const transaction = await transactionForCreateRecord( + app, recordClone, + ); + recordClone.transactionId = transaction.id; + await app.datastore.createFolder(recordClone.key); + await app.datastore.createFolder( + joinKey(recordClone.key, 'files'), + ); + await app.datastore.createJson( + getRecordFileName(recordClone.key), + recordClone, + ); + await initialiseReverseReferenceIndexes(app, record); + await initialiseAncestorIndexes(app, record); + await initialiseChildCollections(app, recordClone.key); + await app.publish(events.recordApi.save.onRecordCreated, { + record: recordClone, + }); + } else { + const oldRecord = await _load(app, recordClone.key); + const transaction = await transactionForUpdateRecord( + app, oldRecord, recordClone, + ); + recordClone.transactionId = transaction.id; + await app.datastore.updateJson( + getRecordFileName(recordClone.key), + recordClone, + ); + + await app.publish(events.recordApi.save.onRecordUpdated, { + old: oldRecord, + new: recordClone, + }); + } + + await app.cleanupTransactions(); + + const returnedClone = cloneDeep(recordClone); + returnedClone.isNew = false; + return returnedClone; +}; + +const initialiseAncestorIndexes = async (app, record) => { + const recordNode = getExactNodeForPath(app.hierarchy)(record.key); + + for (const index of recordNode.indexes) { + const indexKey = joinKey(record.key, index.name); + if (!await app.datastore.exists(indexKey)) { await initialiseIndex(app.datastore, record.key, index); } + } +}; + +const initialiseReverseReferenceIndexes = async (app, record) => { + const recordNode = getExactNodeForPath(app.hierarchy)(record.key); + + const indexNodes = $(fieldsThatReferenceThisRecord(app, recordNode), [ + map(f => $(f.typeOptions.reverseIndexNodeKeys, [ + map(n => getNode( + app.hierarchy, + n, + )), + ])), + flatten, + ]); + + for (const indexNode of indexNodes) { + await initialiseIndex( + app.datastore, record.key, indexNode, + ); + } +}; + +const maintainReferentialIntegrity = async (app, indexingApi, oldRecord, newRecord) => { + /* + FOREACH Field that reference this object + - options Index node that for field + - has options index changed for referenced record? + - FOREACH reverse index of field + - FOREACH referencingRecord in reverse index + - Is field value still pointing to referencedRecord + - Update referencingRecord.fieldName to new value + - Save + */ + const recordNode = getExactNodeForPath(app.hierarchy)(newRecord.key); + const referenceFields = fieldsThatReferenceThisRecord( + app, recordNode, + ); + + const updates = $(referenceFields, [ + map(f => ({ + node: getNode( + app.hierarchy, f.typeOptions.indexNodeKey, + ), + field: f, + })), + map(n => ({ + old: mapRecord(oldRecord, n.node), + new: mapRecord(newRecord, n.node), + indexNode: n.node, + field: n.field, + reverseIndexKeys: $(n.field.typeOptions.reverseIndexNodeKeys, [ + map(k => joinKey( + newRecord.key, + getLastPartInKey(k), + )), + ]), + })), + filter(diff => !isEqual(diff.old)(diff.new)), + ]); + + for (const update of updates) { + for (const reverseIndexKey of update.reverseIndexKeys) { + const rows = await listItems(app)(reverseIndexKey); + + for (const key of map(r => r.key)(rows)) { + const record = await _load(app, key); + if (record[update.field.name].key === newRecord.key) { + record[update.field.name] = update.new; + await _save(app, indexingApi, record, undefined, true); + } + } + } + } +}; + +const fieldsThatReferenceThisRecord = (app, recordNode) => $(app.hierarchy, [ + getFlattenedHierarchy, + filter(isRecord), + map(n => n.fields), + flatten, + filter(fieldReversesReferenceToNode(recordNode)), +]); diff --git a/packages/core/src/recordApi/uploadFile.js b/packages/core/src/recordApi/uploadFile.js new file mode 100644 index 0000000000..4a4a91cec8 --- /dev/null +++ b/packages/core/src/recordApi/uploadFile.js @@ -0,0 +1,128 @@ +import { + includes, filter, + map, some, +} from 'lodash/fp'; +import { generate } from 'shortid'; +import { _load } from './load'; +import { + apiWrapper, events, splitKey, + $, joinKey, isNothing, tryAwaitOrIgnore, +} from '../common'; +import { getExactNodeForPath } from '../templateApi/hierarchy'; +import { permission } from '../authApi/permissions'; +import { isLegalFilename } from '../types/file'; +import { BadRequestError, ForbiddenError } from '../common/errors'; + +export const uploadFile = app => async (recordKey, readableStream, relativeFilePath) => apiWrapper( + app, + events.recordApi.uploadFile, + permission.updateRecord.isAuthorized(recordKey), + { recordKey, readableStream, relativeFilePath }, + _uploadFile, app, recordKey, readableStream, relativeFilePath, +); + +const _uploadFile = async (app, recordKey, readableStream, relativeFilePath) => { + if (isNothing(recordKey)) { throw new BadRequestError('Record Key not supplied'); } + if (isNothing(relativeFilePath)) { throw new BadRequestError('file path not supplied'); } + if (!isLegalFilename(relativeFilePath)) { throw new BadRequestError('Illegal filename'); } + + const record = await _load(app, recordKey); + + const fullFilePath = safeGetFullFilePath( + recordKey, relativeFilePath, + ); + + const tempFilePath = `${fullFilePath}_${generate()}.temp`; + + const outputStream = await app.datastore.writableFileStream( + tempFilePath, + ); + + return new Promise((resolve,reject) => { + readableStream.pipe(outputStream); + outputStream.on('error', reject); + outputStream.on('finish', resolve); + }) + .then(() => app.datastore.getFileSize(tempFilePath)) + .then(size => { + const isExpectedFileSize = checkFileSizeAgainstFields( + app, record, relativeFilePath, size + ); + if (!isExpectedFileSize) { throw new BadRequestError(`Fields for ${relativeFilePath} do not have expected size: ${join(',')(incorrectFields)}`); } + + }) + .then(() => tryAwaitOrIgnore(app.datastore.deleteFile, fullFilePath)) + .then(() => app.datastore.renameFile(tempFilePath, fullFilePath)); + + /* + readableStream.pipe(outputStream); + + await new Promise(fulfill => outputStream.on('finish', fulfill)); + + const isExpectedFileSize = checkFileSizeAgainstFields( + app, + record, relativeFilePath, + await app.datastore.getFileSize(tempFilePath), + ); + + if (!isExpectedFileSize) { + throw new Error( + `Fields for ${relativeFilePath} do not have expected size`); + } + + await tryAwaitOrIgnore(app.datastore.deleteFile, fullFilePath); + + await app.datastore.renameFile(tempFilePath, fullFilePath); + */ +}; + +const checkFileSizeAgainstFields = (app, record, relativeFilePath, expectedSize) => { + const recordNode = getExactNodeForPath(app.hierarchy)(record.key); + + const incorrectFileFields = $(recordNode.fields, [ + filter(f => f.type === 'file' + && record[f.name].relativePath === relativeFilePath + && record[f.name].size !== expectedSize), + map(f => f.name), + ]); + + const incorrectFileArrayFields = $(recordNode.fields, [ + filter(a => a.type === 'array' + && $(record[a.name], [ + some(f => record[f.name].relativePath === relativeFilePath + && record[f.name].size !== expectedSize), + ])), + map(f => f.name), + ]); + + const incorrectFields = [ + ...incorrectFileFields, + ...incorrectFileArrayFields, + ]; + + if (incorrectFields.length > 0) { + return false; + } + + return true; +}; + +export const safeGetFullFilePath = (recordKey, relativeFilePath) => { + const naughtyUser = () => { throw new ForbiddenError('naughty naughty'); }; + + if (relativeFilePath.startsWith('..')) naughtyUser(); + + const pathParts = splitKey(relativeFilePath); + + if (includes('..')(pathParts)) naughtyUser(); + + const recordKeyParts = splitKey(recordKey); + + const fullPathParts = [ + ...recordKeyParts, + 'files', + ...filter(p => p !== '.')(pathParts), + ]; + + return joinKey(fullPathParts); +}; diff --git a/packages/core/src/recordApi/validate.js b/packages/core/src/recordApi/validate.js new file mode 100644 index 0000000000..1e760c42df --- /dev/null +++ b/packages/core/src/recordApi/validate.js @@ -0,0 +1,85 @@ +import { + map, reduce, filter, + isEmpty, flatten, each, +} from 'lodash/fp'; +import { compileExpression } from '@nx-js/compiler-util'; +import _ from 'lodash'; +import { getExactNodeForPath } from '../templateApi/hierarchy'; +import { validateFieldParse, validateTypeConstraints } from '../types'; +import { $, isNothing, isNonEmptyString } from '../common'; +import { _getContext } from './getContext'; + +const fieldParseError = (fieldName, value) => ({ + fields: [fieldName], + message: `Could not parse field ${fieldName}:${value}`, +}); + +const validateAllFieldParse = (record, recordNode) => $(recordNode.fields, [ + map(f => ({ name: f.name, parseResult: validateFieldParse(f, record) })), + reduce((errors, f) => { + if (f.parseResult.success) return errors; + errors.push( + fieldParseError(f.name, f.parseResult.value), + ); + return errors; + }, []), +]); + +const validateAllTypeConstraints = async (record, recordNode, context) => { + const errors = []; + for (const field of recordNode.fields) { + $(await validateTypeConstraints(field, record, context), [ + filter(isNonEmptyString), + map(m => ({ message: m, fields: [field.name] })), + each(e => errors.push(e)), + ]); + } + return errors; +}; + +const runRecordValidationRules = (record, recordNode) => { + const runValidationRule = (rule) => { + const isValid = compileExpression(rule.expressionWhenValid); + const expressionContext = { record, _ }; + return (isValid(expressionContext) + ? { valid: true } + : ({ + valid: false, + fields: rule.invalidFields, + message: rule.messageWhenInvalid, + })); + }; + + return $(recordNode.validationRules, [ + map(runValidationRule), + flatten, + filter(r => r.valid === false), + map(r => ({ fields: r.fields, message: r.message })), + ]); +}; + +export const validate = app => async (record, context) => { + context = isNothing(context) + ? _getContext(app, record.key) + : context; + + const recordNode = getExactNodeForPath(app.hierarchy)(record.key); + const fieldParseFails = validateAllFieldParse(record, recordNode); + + // non parsing would cause further issues - exit here + if (!isEmpty(fieldParseFails)) { return ({ isValid: false, errors: fieldParseFails }); } + + const recordValidationRuleFails = runRecordValidationRules(record, recordNode); + const typeContraintFails = await validateAllTypeConstraints(record, recordNode, context); + + if (isEmpty(fieldParseFails) + && isEmpty(recordValidationRuleFails) + && isEmpty(typeContraintFails)) { + return ({ isValid: true, errors: [] }); + } + + return ({ + isValid: false, + errors: _.union(fieldParseFails, typeContraintFails, recordValidationRuleFails), + }); +}; diff --git a/packages/core/src/templateApi/createActions.js b/packages/core/src/templateApi/createActions.js new file mode 100644 index 0000000000..8cefa5c881 --- /dev/null +++ b/packages/core/src/templateApi/createActions.js @@ -0,0 +1,23 @@ + +export const createTrigger = () => ({ + actionName: '', + eventName: '', + // function, has access to event context, + // returns object that is used as parameter to action + // only used if triggered by event + optionsCreator: '', + // action runs if true, + // has access to event context + condition: '', +}); + +export const createAction = () => ({ + name: '', + behaviourSource: '', + // name of function in actionSource + behaviourName: '', + // parameter passed into behaviour. + // any other parms passed at runtime e.g. + // by trigger, or manually, will be merged into this + initialOptions: {}, +}); diff --git a/packages/core/src/templateApi/createNodes.js b/packages/core/src/templateApi/createNodes.js new file mode 100644 index 0000000000..bc284c4a72 --- /dev/null +++ b/packages/core/src/templateApi/createNodes.js @@ -0,0 +1,223 @@ +import { each, constant, find } from 'lodash'; +import { map, max } from 'lodash/fp'; +import { + switchCase, defaultCase, joinKey, + $, isNothing, isSomething, +} from '../common'; +import { + isIndex, isRoot, isSingleRecord, isCollectionRecord, + isRecord, isaggregateGroup, + getFlattenedHierarchy, +} from './hierarchy'; +import { all } from '../types'; +import { BadRequestError } from '../common/errors'; + +export const createNodeErrors = { + indexCannotBeParent: 'Index template cannot be a parent', + allNonRootNodesMustHaveParent: 'Only the root node may have no parent', + indexParentMustBeRecordOrRoot: 'An index may only have a record or root as a parent', + aggregateParentMustBeAnIndex: 'aggregateGroup parent must be an index', +}; + +const pathRegxMaker = node => () => node.nodeKey().replace(/{id}/g, '[a-zA-Z0-9_-]+'); + +const nodeKeyMaker = node => () => switchCase( + + [n => isRecord(n) && !isSingleRecord(n), + n => joinKey( + node.parent().nodeKey(), + node.collectionName, + `${n.nodeId}-{id}`, + )], + + [isRoot, + constant('/')], + + [defaultCase, + n => joinKey(node.parent().nodeKey(), n.name)], + +)(node); + + +const validate = parent => (node) => { + if (isIndex(node) + && isSomething(parent) + && !isRoot(parent) + && !isRecord(parent)) { + throw new BadRequestError(createNodeErrors.indexParentMustBeRecordOrRoot); + } + + if (isaggregateGroup(node) + && isSomething(parent) + && !isIndex(parent)) { + throw new BadRequestError(createNodeErrors.aggregateParentMustBeAnIndex); + } + + if (isNothing(parent) && !isRoot(node)) { throw new BadRequestError(createNodeErrors.allNonRootNodesMustHaveParent); } + + return node; +}; + +const construct = parent => (node) => { + node.nodeKey = nodeKeyMaker(node); + node.pathRegx = pathRegxMaker(node); + node.parent = constant(parent); + node.isRoot = () => isNothing(parent) + && node.name === 'root' + && node.type === 'root'; + if (isCollectionRecord(node)) { + node.collectionNodeKey = () => joinKey( + parent.nodeKey(), node.collectionName, + ); + node.collectionPathRegx = () => joinKey( + parent.pathRegx(), node.collectionName, + ); + } + return node; +}; + +const addToParent = (obj) => { + const parent = obj.parent(); + if (isSomething(parent)) { + if (isIndex(obj)) + // Q: why are indexes not children ? + // A: because they cannot have children of their own. + { parent.indexes.push(obj); } else if (isaggregateGroup(obj)) { parent.aggregateGroups.push(obj); } else { parent.children.push(obj); } + + if (isRecord(obj)) { + const defaultIndex = find( + parent.indexes, + i => i.name === `${parent.name}_index`, + ); + if (defaultIndex) { + defaultIndex.allowedRecordNodeIds.push(obj.nodeId); + } + } + } + return obj; +}; + +export const constructNode = (parent, obj) => $(obj, [ + construct(parent), + validate(parent), + addToParent, +]); + +const getNodeId = (parentNode) => { + // this case is handled better elsewhere + if (!parentNode) return null; + const findRoot = n => (isRoot(n) ? n : findRoot(n.parent())); + const root = findRoot(parentNode); + + return ($(root, [ + getFlattenedHierarchy, + map(n => n.nodeId), + max]) + 1); +}; + +export const constructHierarchy = (node, parent) => { + construct(parent)(node); + if (node.indexes) { + each(node.indexes, + child => constructHierarchy(child, node)); + } + if (node.aggregateGroups) { + each(node.aggregateGroups, + child => constructHierarchy(child, node)); + } + if (node.children && node.children.length > 0) { + each(node.children, + child => constructHierarchy(child, node)); + } + if (node.fields) { + each(node.fields, + f => each(f.typeOptions, (val, key) => { + const def = all[f.type].optionDefinitions[key]; + if (!def) { + // unknown typeOption + delete f.typeOptions[key]; + } else { + f.typeOptions[key] = def.parse(val); + } + })); + } + return node; +}; + + +export const getNewRootLevel = () => construct()({ + name: 'root', + type: 'root', + children: [], + pathMaps: [], + indexes: [], + nodeId: 0, +}); + +const _getNewRecordTemplate = (parent, name, createDefaultIndex, isSingle) => { + const node = constructNode(parent, { + name, + type: 'record', + fields: [], + children: [], + validationRules: [], + nodeId: getNodeId(parent), + indexes: [], + allidsShardFactor: isRecord(parent) ? 1 : 64, + collectionName: '', + isSingle, + }); + + if (createDefaultIndex) { + const defaultIndex = getNewIndexTemplate(parent); + defaultIndex.name = `${name}_index`; + defaultIndex.allowedRecordNodeIds.push(node.nodeId); + } + + return node; +}; + +export const getNewRecordTemplate = (parent, name = '', createDefaultIndex = true) => _getNewRecordTemplate(parent, name, createDefaultIndex, false); + +export const getNewSingleRecordTemplate = parent => _getNewRecordTemplate(parent, '', false, true); + +export const getNewIndexTemplate = (parent, type = 'ancestor') => constructNode(parent, { + name: '', + type: 'index', + map: 'return {...record};', + filter: '', + indexType: type, + getShardName: '', + getSortKey: 'record.id', + aggregateGroups: [], + allowedRecordNodeIds: [], + nodeId: getNodeId(parent), +}); + +export const getNewAggregateGroupTemplate = index => constructNode(index, { + name: '', + type: 'aggregateGroup', + groupBy: '', + aggregates: [], + condition: '', + nodeId: getNodeId(index), +}); + +export const getNewAggregateTemplate = (set) => { + const aggregatedValue = { + name: '', + aggregatedValue: '', + }; + set.aggregates.push(aggregatedValue); + return aggregatedValue; +}; + +export default { + getNewRootLevel, + getNewRecordTemplate, + getNewIndexTemplate, + createNodeErrors, + constructHierarchy, + getNewAggregateGroupTemplate, + getNewAggregateTemplate, +}; diff --git a/packages/core/src/templateApi/deleteNodes.js b/packages/core/src/templateApi/deleteNodes.js new file mode 100644 index 0000000000..1de30fb479 --- /dev/null +++ b/packages/core/src/templateApi/deleteNodes.js @@ -0,0 +1,12 @@ +import { +} from '../templateApi/heirarchy'; + +export const canDelete = (app, node) => { + + /* + it must not exist on any index.allowedRecordNodeIds + it must not exist on and reference type fields + these rules should apply to any child nodes , which will also be deleted + */ + +}; diff --git a/packages/core/src/templateApi/fields.js b/packages/core/src/templateApi/fields.js new file mode 100644 index 0000000000..dc4f9ba491 --- /dev/null +++ b/packages/core/src/templateApi/fields.js @@ -0,0 +1,87 @@ +import { + some, map, filter, keys, includes, + countBy, flatten, +} from 'lodash/fp'; +import { + isSomething, $, + isNonEmptyString, + isNothingOrEmpty, + isNothing, +} from '../common'; +import { all, getDefaultOptions } from '../types'; +import { applyRuleSet, makerule } from '../common/validationCommon'; +import { BadRequestError } from '../common/errors'; + +export const fieldErrors = { + AddFieldValidationFailed: 'Add field validation: ', +}; + +export const allowedTypes = () => keys(all); + +export const getNewField = type => ({ + name: '', // how field is referenced internally + type, + typeOptions: getDefaultOptions(type), + label: '', // how field is displayed + getInitialValue: 'default', // function that gets value when initially created + getUndefinedValue: 'default', // function that gets value when field undefined on record +}); + +const fieldRules = allFields => [ + makerule('name', 'field name is not set', + f => isNonEmptyString(f.name)), + makerule('type', 'field type is not set', + f => isNonEmptyString(f.type)), + makerule('label', 'field label is not set', + f => isNonEmptyString(f.label)), + makerule('getInitialValue', 'getInitialValue function is not set', + f => isNonEmptyString(f.getInitialValue)), + makerule('getUndefinedValue', 'getUndefinedValue function is not set', + f => isNonEmptyString(f.getUndefinedValue)), + makerule('name', 'field name is duplicated', + f => isNothingOrEmpty(f.name) + || countBy('name')(allFields)[f.name] === 1), + makerule('type', 'type is unknown', + f => isNothingOrEmpty(f.type) + || some(t => f.type === t)(allowedTypes())), +]; + +const typeOptionsRules = (field) => { + const type = all[field.type]; + if (isNothing(type)) return []; + + const def = optName => type.optionDefinitions[optName]; + + return $(field.typeOptions, [ + keys, + filter(o => isSomething(def(o)) + && isSomething(def(o).isValid)), + map(o => makerule( + `typeOptions.${o}`, + `${def(o).requirementDescription}`, + field => def(o).isValid(field.typeOptions[o]), + )), + ]); +}; + +export const validateField = allFields => (field) => { + const everySingleField = includes(field)(allFields) ? allFields : [...allFields, field]; + return applyRuleSet([...fieldRules(everySingleField), ...typeOptionsRules(field)])(field); +}; + +export const validateAllFields = recordNode => $(recordNode.fields, [ + map(validateField(recordNode.fields)), + flatten, +]); + +export const addField = (recordTemplate, field) => { + if (isNothingOrEmpty(field.label)) { + field.label = field.name; + } + const validationMessages = validateField([...recordTemplate.fields, field])(field); + if (validationMessages.length > 0) { + const errors = map(m => m.error)(validationMessages); + throw new BadRequestError(`${fieldErrors.AddFieldValidationFailed} ${errors.join(', ')}`); + } + recordTemplate.fields.push(field); +}; diff --git a/packages/core/src/templateApi/getApplicationDefinition.js b/packages/core/src/templateApi/getApplicationDefinition.js new file mode 100644 index 0000000000..49862090d2 --- /dev/null +++ b/packages/core/src/templateApi/getApplicationDefinition.js @@ -0,0 +1,14 @@ +import { appDefinitionFile } from '../common'; +import { constructHierarchy } from './createNodes'; + +export const getApplicationDefinition = datastore => async () => { + const exists = await datastore.exists(appDefinitionFile); + + if (!exists) throw new Error('Application definition does not exist'); + + const appDefinition = await datastore.loadJson(appDefinitionFile); + appDefinition.hierarchy = constructHierarchy( + appDefinition.hierarchy, + ); + return appDefinition; +}; diff --git a/packages/core/src/templateApi/getBehaviourSources.js b/packages/core/src/templateApi/getBehaviourSources.js new file mode 100644 index 0000000000..80996578a0 --- /dev/null +++ b/packages/core/src/templateApi/getBehaviourSources.js @@ -0,0 +1,4 @@ + +export const getBehaviourSources = async (datastore) => { + await datastore.loadFile('/.config/behaviourSources.js'); +}; diff --git a/packages/core/src/templateApi/hierarchy.js b/packages/core/src/templateApi/hierarchy.js new file mode 100644 index 0000000000..dec1bf90c7 --- /dev/null +++ b/packages/core/src/templateApi/hierarchy.js @@ -0,0 +1,234 @@ +import { + find, constant, map, last, + first, split, intersection, take, + union, includes, filter, some, +} from 'lodash/fp'; +import { + $, switchCase, isNothing, isSomething, + defaultCase, splitKey, isNonEmptyString, + joinKey, getHashCode, +} from '../common'; +import { indexTypes } from './indexes'; + +export const getFlattenedHierarchy = (appHierarchy, useCached = true) => { + if (isSomething(appHierarchy.getFlattenedHierarchy) && useCached) { return appHierarchy.getFlattenedHierarchy(); } + + const flattenHierarchy = (currentNode, flattened) => { + flattened.push(currentNode); + if ((!currentNode.children + || currentNode.children.length === 0) + && (!currentNode.indexes + || currentNode.indexes.length === 0) + && (!currentNode.aggregateGroups + || currentNode.aggregateGroups.length === 0)) { + return flattened; + } + + const unionIfAny = l2 => l1 => union(l1)(!l2 ? [] : l2); + + const children = $([], [ + unionIfAny(currentNode.children), + unionIfAny(currentNode.indexes), + unionIfAny(currentNode.aggregateGroups), + ]); + + for (const child of children) { + flattenHierarchy(child, flattened); + } + return flattened; + }; + + appHierarchy.getFlattenedHierarchy = () => flattenHierarchy(appHierarchy, []); + return appHierarchy.getFlattenedHierarchy(); +}; + +export const getLastPartInKey = key => last(splitKey(key)); + +export const getNodesInPath = appHierarchy => key => $(appHierarchy, [ + getFlattenedHierarchy, + filter(n => new RegExp(`${n.pathRegx()}`).test(key)), +]); + +export const getExactNodeForPath = appHierarchy => key => $(appHierarchy, [ + getFlattenedHierarchy, + find(n => new RegExp(`${n.pathRegx()}$`).test(key)), +]); + +export const getNodeForCollectionPath = appHierarchy => collectionKey => $(appHierarchy, [ + getFlattenedHierarchy, + find(n => (isCollectionRecord(n) + && new RegExp(`${n.collectionPathRegx()}$`).test(collectionKey))), +]); + +export const hasMatchingAncestor = ancestorPredicate => decendantNode => switchCase( + + [node => isNothing(node.parent()), + constant(false)], + + [node => ancestorPredicate(node.parent()), + constant(true)], + + [defaultCase, + node => hasMatchingAncestor(ancestorPredicate)(node.parent())], + +)(decendantNode); + +export const getNode = (appHierarchy, nodeKey) => $(appHierarchy, [ + getFlattenedHierarchy, + find(n => n.nodeKey() === nodeKey + || (isCollectionRecord(n) + && n.collectionNodeKey() === nodeKey)), +]); + +export const getCollectionNode = (appHierarchy, nodeKey) => $(appHierarchy, [ + getFlattenedHierarchy, + find(n => (isCollectionRecord(n) + && n.collectionNodeKey() === nodeKey)), +]); + +export const getNodeByKeyOrNodeKey = (appHierarchy, keyOrNodeKey) => { + const nodeByKey = getExactNodeForPath(appHierarchy)(keyOrNodeKey); + return isNothing(nodeByKey) + ? getNode(appHierarchy, keyOrNodeKey) + : nodeByKey; +}; + +export const getCollectionNodeByKeyOrNodeKey = (appHierarchy, keyOrNodeKey) => { + const nodeByKey = getNodeForCollectionPath(appHierarchy)(keyOrNodeKey); + return isNothing(nodeByKey) + ? getCollectionNode(appHierarchy, keyOrNodeKey) + : nodeByKey; +}; + +export const isNode = (appHierarchy, key) => isSomething(getExactNodeForPath(appHierarchy)(key)); + +export const getActualKeyOfParent = (parentNodeKey, actualChildKey) => $(actualChildKey, [ + splitKey, + take(splitKey(parentNodeKey).length), + ks => joinKey(...ks), +]); + +export const getParentKey = (key) => { + return $(key, [ + splitKey, + take(splitKey(key).length - 1), + joinKey, + ]); +}; + +export const isKeyAncestorOf = ancestorKey => decendantNode => hasMatchingAncestor(p => p.nodeKey() === ancestorKey)(decendantNode); + +export const hasNoMatchingAncestors = parentPredicate => node => !hasMatchingAncestor(parentPredicate)(node); + +export const findField = (recordNode, fieldName) => find(f => f.name == fieldName)(recordNode.fields); + +export const isAncestor = decendant => ancestor => isKeyAncestorOf(ancestor.nodeKey())(decendant); + +export const isDecendant = ancestor => decendant => isAncestor(decendant)(ancestor); + +export const getRecordNodeId = recordKey => $(recordKey, [ + splitKey, + last, + getRecordNodeIdFromId, +]); + +export const getRecordNodeIdFromId = recordId => $(recordId, [split('-'), first, parseInt]); + +export const getRecordNodeById = (hierarchy, recordId) => $(hierarchy, [ + getFlattenedHierarchy, + find(n => isRecord(n) + && n.nodeId === getRecordNodeIdFromId(recordId)), +]); + +export const recordNodeIdIsAllowed = indexNode => nodeId => indexNode.allowedRecordNodeIds.length === 0 + || includes(nodeId)(indexNode.allowedRecordNodeIds); + +export const recordNodeIsAllowed = indexNode => recordNode => recordNodeIdIsAllowed(indexNode)(recordNode.nodeId); + +export const getAllowedRecordNodesForIndex = (appHierarchy, indexNode) => { + const recordNodes = $(appHierarchy, [ + getFlattenedHierarchy, + filter(isRecord), + ]); + + if (isGlobalIndex(indexNode)) { + return $(recordNodes, [ + filter(recordNodeIsAllowed(indexNode)), + ]); + } + + if (isAncestorIndex(indexNode)) { + return $(recordNodes, [ + filter(isDecendant(indexNode.parent())), + filter(recordNodeIsAllowed(indexNode)), + ]); + } + + if (isReferenceIndex(indexNode)) { + return $(recordNodes, [ + filter(n => some(fieldReversesReferenceToIndex(indexNode))(n.fields)), + ]); + } +}; + +export const getNodeFromNodeKeyHash = hierarchy => hash => $(hierarchy, [ + getFlattenedHierarchy, + find(n => getHashCode(n.nodeKey()) === hash), +]); + +export const isRecord = node => isSomething(node) && node.type === 'record'; +export const isSingleRecord = node => isRecord(node) && node.isSingle; +export const isCollectionRecord = node => isRecord(node) && !node.isSingle; +export const isIndex = node => isSomething(node) && node.type === 'index'; +export const isaggregateGroup = node => isSomething(node) && node.type === 'aggregateGroup'; +export const isShardedIndex = node => isIndex(node) && isNonEmptyString(node.getShardName); +export const isRoot = node => isSomething(node) && node.isRoot(); +export const isDecendantOfARecord = hasMatchingAncestor(isRecord); +export const isGlobalIndex = node => isIndex(node) && isRoot(node.parent()); +export const isReferenceIndex = node => isIndex(node) && node.indexType === indexTypes.reference; +export const isAncestorIndex = node => isIndex(node) && node.indexType === indexTypes.ancestor; + +export const fieldReversesReferenceToNode = node => field => field.type === 'reference' + && intersection(field.typeOptions.reverseIndexNodeKeys)(map(i => i.nodeKey())(node.indexes)) + .length > 0; + +export const fieldReversesReferenceToIndex = indexNode => field => field.type === 'reference' + && intersection(field.typeOptions.reverseIndexNodeKeys)([indexNode.nodeKey()]) + .length > 0; + +export default { + getLastPartInKey, + getNodesInPath, + getExactNodeForPath, + hasMatchingAncestor, + getNode, + getNodeByKeyOrNodeKey, + isNode, + getActualKeyOfParent, + getParentKey, + isKeyAncestorOf, + hasNoMatchingAncestors, + findField, + isAncestor, + isDecendant, + getRecordNodeId, + getRecordNodeIdFromId, + getRecordNodeById, + recordNodeIdIsAllowed, + recordNodeIsAllowed, + getAllowedRecordNodesForIndex, + getNodeFromNodeKeyHash, + isRecord, + isCollectionRecord, + isIndex, + isaggregateGroup, + isShardedIndex, + isRoot, + isDecendantOfARecord, + isGlobalIndex, + isReferenceIndex, + isAncestorIndex, + fieldReversesReferenceToNode, + fieldReversesReferenceToIndex, + getFlattenedHierarchy, +}; diff --git a/packages/core/src/templateApi/index.js b/packages/core/src/templateApi/index.js new file mode 100644 index 0000000000..efb3805d36 --- /dev/null +++ b/packages/core/src/templateApi/index.js @@ -0,0 +1,64 @@ +import { + getNewRootLevel, + getNewRecordTemplate, getNewIndexTemplate, + createNodeErrors, constructHierarchy, + getNewAggregateGroupTemplate, getNewSingleRecordTemplate, + getNewAggregateTemplate, constructNode, +} + from './createNodes'; +import { + getNewField, validateField, + addField, fieldErrors, +} from './fields'; +import { + getNewRecordValidationRule, commonRecordValidationRules, + addRecordValidationRule, +} from './recordValidationRules'; +import { createAction, createTrigger } from './createActions'; +import { + validateTriggers, validateTrigger, validateNode, + validateActions, validateAll, +} from './validate'; +import { getApplicationDefinition } from './getApplicationDefinition'; +import { saveApplicationHierarchy } from './saveApplicationHierarchy'; +import { saveActionsAndTriggers } from './saveActionsAndTriggers'; +import { all } from '../types'; +import { getBehaviourSources } from "./getBehaviourSources"; + +const api = app => ({ + + getApplicationDefinition: getApplicationDefinition(app.datastore), + saveApplicationHierarchy: saveApplicationHierarchy(app), + saveActionsAndTriggers: saveActionsAndTriggers(app), + getBehaviourSources: () => getBehaviourSources(app.datastore), + getNewRootLevel, + constructNode, + getNewIndexTemplate, + getNewRecordTemplate, + getNewField, + validateField, + addField, + fieldErrors, + getNewRecordValidationRule, + commonRecordValidationRules, + addRecordValidationRule, + createAction, + createTrigger, + validateActions, + validateTrigger, + getNewAggregateGroupTemplate, + getNewAggregateTemplate, + constructHierarchy, + getNewSingleRecordTemplate, + allTypes: all, + validateNode, + validateAll, + validateTriggers, +}); + + +export const getTemplateApi = app => api(app); + +export const errors = createNodeErrors; + +export default getTemplateApi; diff --git a/packages/core/src/templateApi/indexes.js b/packages/core/src/templateApi/indexes.js new file mode 100644 index 0000000000..b89d2d5745 --- /dev/null +++ b/packages/core/src/templateApi/indexes.js @@ -0,0 +1,38 @@ +import { + map, isEmpty, countBy, flatten, includes, +} from 'lodash/fp'; +import { join, keys } from 'lodash'; +import { applyRuleSet, makerule } from '../common/validationCommon'; +import { compileFilter, compileMap } from '../indexing/evaluate'; +import { isNonEmptyString, executesWithoutException, $ } from '../common'; +import { isRecord } from './hierarchy'; + +export const indexTypes = { reference: 'reference', ancestor: 'ancestor' }; + +export const indexRuleSet = [ + makerule('map', 'index has no map function', + index => isNonEmptyString(index.map)), + makerule('map', "index's map function does not compile", + index => !isNonEmptyString(index.map) + || executesWithoutException(() => compileMap(index))), + makerule('filter', "index's filter function does not compile", + index => !isNonEmptyString(index.filter) + || executesWithoutException(() => compileFilter(index))), + makerule('name', 'must declare a name for index', + index => isNonEmptyString(index.name)), + makerule('name', 'there is a duplicate named index on this node', + index => isEmpty(index.name) + || countBy('name')(index.parent().indexes)[index.name] === 1), + makerule('indexType', 'reference index may only exist on a record node', + index => isRecord(index.parent()) + || index.indexType !== indexTypes.reference), + makerule('indexType', `index type must be one of: ${join(', ', keys(indexTypes))}`, + index => includes(index.indexType)(keys(indexTypes))), +]; + +export const validateIndex = (index, allReferenceIndexesOnNode) => applyRuleSet(indexRuleSet(allReferenceIndexesOnNode))(index); + +export const validateAllIndexes = node => $(node.indexes, [ + map(i => validateIndex(i, node.indexes)), + flatten, +]); diff --git a/packages/core/src/templateApi/recordValidationRules.js b/packages/core/src/templateApi/recordValidationRules.js new file mode 100644 index 0000000000..c59f70a6cc --- /dev/null +++ b/packages/core/src/templateApi/recordValidationRules.js @@ -0,0 +1,37 @@ +import { isNumber, isBoolean, defaultCase } from 'lodash/fp'; +import { switchCase } from '../common'; + +export const getNewRecordValidationRule = (invalidFields, + messageWhenInvalid, + expressionWhenValid) => ({ + invalidFields, messageWhenInvalid, expressionWhenValid, +}); + +const getStaticValue = switchCase( + [isNumber, v => v.toString()], + [isBoolean, v => v.toString()], + [defaultCase, v => `'${v}'`], +); + +export const commonRecordValidationRules = ({ + + fieldNotEmpty: fieldName => getNewRecordValidationRule( + [fieldName], + `${fieldName} is empty`, + `!_.isEmpty(record['${fieldName}'])`, + ), + + fieldBetween: (fieldName, min, max) => getNewRecordValidationRule( + [fieldName], + `${fieldName} must be between ${min.toString()} and ${max.toString()}`, + `record['${fieldName}'] >= ${getStaticValue(min)} && record['${fieldName}'] <= ${getStaticValue(max)} `, + ), + + fieldGreaterThan: (fieldName, min, max) => getNewRecordValidationRule( + [fieldName], + `${fieldName} must be greater than ${min.toString()} and ${max.toString()}`, + `record['${fieldName}'] >= ${getStaticValue(min)} `, + ), +}); + +export const addRecordValidationRule = recordNode => rule => recordNode.validationRules.push(rule); diff --git a/packages/core/src/templateApi/saveActionsAndTriggers.js b/packages/core/src/templateApi/saveActionsAndTriggers.js new file mode 100644 index 0000000000..23c976cb03 --- /dev/null +++ b/packages/core/src/templateApi/saveActionsAndTriggers.js @@ -0,0 +1,40 @@ +import { join } from 'lodash'; +import { map } from 'lodash/fp'; +import { appDefinitionFile } from '../common'; +import { validateTriggers, validateActions } from './validate'; +import { apiWrapper } from '../common/apiWrapper'; +import { events } from '../common/events'; +import { permission } from '../authApi/permissions'; +import { BadRequestError } from '../common/errors'; + +export const saveActionsAndTriggers = app => async (actions, triggers) => apiWrapper( + app, + events.templateApi.saveActionsAndTriggers, + permission.writeTemplates.isAuthorized, + { actions, triggers }, + _saveActionsAndTriggers, app.datastore, actions, triggers, +); + +export const _saveActionsAndTriggers = async (datastore, actions, triggers) => { + if (await datastore.exists(appDefinitionFile)) { + const appDefinition = await datastore.loadJson(appDefinitionFile); + appDefinition.actions = actions; + appDefinition.triggers = triggers; + + const actionValidErrs = map(e => e.error)(validateActions(actions)); + + if (actionValidErrs.length > 0) { + throw new BadRequestError(`Actions are invalid: ${join(actionValidErrs, ', ')}`); + } + + const triggerValidErrs = map(e => e.error)(validateTriggers(triggers, actions)); + + if (triggerValidErrs.length > 0) { + throw new BadRequestError(`Triggers are invalid: ${join(triggerValidErrs, ', ')}`); + } + + await datastore.updateJson(appDefinitionFile, appDefinition); + } else { + throw new BadRequestError('Cannot save actions: Application definition does not exist'); + } +}; diff --git a/packages/core/src/templateApi/saveApplicationHierarchy.js b/packages/core/src/templateApi/saveApplicationHierarchy.js new file mode 100644 index 0000000000..0ec746963a --- /dev/null +++ b/packages/core/src/templateApi/saveApplicationHierarchy.js @@ -0,0 +1,35 @@ +import { join } from 'lodash'; +import { permission } from '../authApi/permissions'; +import { appDefinitionFile } from '../common'; +import { validateAll } from './validate'; +import { apiWrapper } from '../common/apiWrapper'; +import { events } from '../common/events'; + +export const saveApplicationHierarchy = app => async hierarchy => apiWrapper( + app, + events.templateApi.saveApplicationHierarchy, + permission.writeTemplates.isAuthorized, + { hierarchy }, + _saveApplicationHierarchy, app.datastore, hierarchy, +); + + +export const _saveApplicationHierarchy = async (datastore, hierarchy) => { + const validationErrors = await validateAll(hierarchy); + if (validationErrors.length > 0) { + throw new Error(`Hierarchy is invalid: ${join( + validationErrors.map(e => `${e.item.nodeKey ? e.item.nodeKey() : ''} : ${e.error}`), + ',', + )}`); + } + + if (await datastore.exists(appDefinitionFile)) { + const appDefinition = await datastore.loadJson(appDefinitionFile); + appDefinition.hierarchy = hierarchy; + await datastore.updateJson(appDefinitionFile, appDefinition); + } else { + await datastore.createFolder('/.config'); + const appDefinition = { actions: [], triggers: [], hierarchy }; + await datastore.createJson(appDefinitionFile, appDefinition); + } +}; diff --git a/packages/core/src/templateApi/validate.js b/packages/core/src/templateApi/validate.js new file mode 100644 index 0000000000..d037e2cfa8 --- /dev/null +++ b/packages/core/src/templateApi/validate.js @@ -0,0 +1,183 @@ +import { + filter, union, constant, + map, flatten, every, uniqBy, + some, includes, isEmpty, +} from 'lodash/fp'; +import { has } from 'lodash'; +import { compileExpression, compileCode } from '@nx-js/compiler-util'; +import { + $, isSomething, switchCase, + anyTrue, isNonEmptyArray, executesWithoutException, + isNonEmptyString, defaultCase, +} from '../common'; +import { + isRecord, isRoot, isaggregateGroup, + isIndex, getFlattenedHierarchy, +} from './hierarchy'; +import { eventsList } from '../common/events'; +import { validateAllFields } from './fields'; +import { + applyRuleSet, makerule, stringNotEmpty, + validationError, +} from '../common/validationCommon'; +import { indexRuleSet } from './indexes'; +import { validateAllAggregates } from './validateAggregate'; + +export const ruleSet = (...sets) => constant(flatten([...sets])); + +const commonRules = [ + makerule('name', 'node name is not set', + node => stringNotEmpty(node.name)), + makerule('type', 'node type not recognised', + anyTrue(isRecord, isRoot, isIndex, isaggregateGroup)), +]; + +const recordRules = [ + makerule('fields', 'no fields have been added to the record', + node => isNonEmptyArray(node.fields)), + makerule('validationRules', "validation rule is missing a 'messageWhenValid' member", + node => every(r => has(r, 'messageWhenInvalid'))(node.validationRules)), + makerule('validationRules', "validation rule is missing a 'expressionWhenValid' member", + node => every(r => has(r, 'expressionWhenValid'))(node.validationRules)), +]; + + +const aggregateGroupRules = [ + makerule('condition', 'condition does not compile', + a => isEmpty(a.condition) + || executesWithoutException( + () => compileExpression(a.condition), + )), +]; + +const getRuleSet = node => switchCase( + + [isRecord, ruleSet( + commonRules, + recordRules, + )], + + [isIndex, ruleSet( + commonRules, + indexRuleSet, + )], + + [isaggregateGroup, ruleSet( + commonRules, + aggregateGroupRules, + )], + + [defaultCase, ruleSet(commonRules, [])], +)(node); + +export const validateNode = node => applyRuleSet(getRuleSet(node))(node); + +export const validateAll = (appHierarchy) => { + const flattened = getFlattenedHierarchy( + appHierarchy, + ); + + const duplicateNameRule = makerule( + 'name', 'node names must be unique under shared parent', + n => filter(f => f.parent() === n.parent() + && f.name === n.name)(flattened).length === 1, + ); + + const duplicateNodeKeyErrors = $(flattened, [ + map(n => applyRuleSet([duplicateNameRule])(n)), + filter(isSomething), + flatten, + ]); + + const fieldErrors = $(flattened, [ + filter(isRecord), + map(validateAllFields), + flatten, + ]); + + const aggregateErrors = $(flattened, [ + filter(isaggregateGroup), + map(s => validateAllAggregates( + s.aggregates, + )), + flatten, + ]); + + return $(flattened, [ + map(validateNode), + flatten, + union(duplicateNodeKeyErrors), + union(fieldErrors), + union(aggregateErrors), + ]); +}; + +const actionRules = [ + makerule('name', 'action must have a name', + a => isNonEmptyString(a.name)), + makerule('behaviourName', 'must supply a behaviour name to the action', + a => isNonEmptyString(a.behaviourName)), + makerule('behaviourSource', 'must supply a behaviour source for the action', + a => isNonEmptyString(a.behaviourSource)), +]; + +const duplicateActionRule = makerule('', 'action name must be unique', () => {}); + +const validateAction = action => applyRuleSet(actionRules)(action); + + +export const validateActions = (allActions) => { + const duplicateActions = $(allActions, [ + filter(a => filter(a2 => a2.name === a.name)(allActions).length > 1), + map(a => validationError(duplicateActionRule, a)), + ]); + + const errors = $(allActions, [ + map(validateAction), + flatten, + union(duplicateActions), + uniqBy('name'), + ]); + + return errors; +}; + +const triggerRules = actions => ([ + makerule('actionName', 'must specify an action', + t => isNonEmptyString(t.actionName)), + makerule('eventName', 'must specify and event', + t => isNonEmptyString(t.eventName)), + makerule('actionName', 'specified action not supplied', + t => !t.actionName + || some(a => a.name === t.actionName)(actions)), + makerule('eventName', 'invalid Event Name', + t => !t.eventName + || includes(t.eventName)(eventsList)), + makerule('optionsCreator', 'Options Creator does not compile - check your expression', + (t) => { + if (!t.optionsCreator) return true; + try { + compileCode(t.optionsCreator); + return true; + } catch (_) { return false; } + }), + makerule('condition', 'Trigger condition does not compile - check your expression', + (t) => { + if (!t.condition) return true; + try { + compileExpression(t.condition); + return true; + } catch (_) { return false; } + }), +]); + +export const validateTrigger = (trigger, allActions) => { + const errors = applyRuleSet(triggerRules(allActions))(trigger); + + return errors; +}; + +export const validateTriggers = (triggers, allActions) => $(triggers, [ + map(t => validateTrigger(t, allActions)), + flatten, +]); diff --git a/packages/core/src/templateApi/validateAggregate.js b/packages/core/src/templateApi/validateAggregate.js new file mode 100644 index 0000000000..78e8abdd72 --- /dev/null +++ b/packages/core/src/templateApi/validateAggregate.js @@ -0,0 +1,25 @@ +import { flatten, map } from 'lodash/fp'; +import { isEmpty } from 'lodash'; +import { compileCode } from '@nx-js/compiler-util'; +import { + isNonEmptyString, + executesWithoutException, $, +} from '../common'; +import { applyRuleSet, makerule } from '../common/validationCommon'; + +const aggregateRules = [ + makerule('name', 'choose a name for the aggregate', + a => isNonEmptyString(a.name)), + makerule('aggregatedValue', 'aggregatedValue does not compile', + a => isEmpty(a.aggregatedValue) + || executesWithoutException( + () => compileCode(a.aggregatedValue), + )), +]; + +export const validateAggregate = aggregate => applyRuleSet(aggregateRules)(aggregate); + +export const validateAllAggregates = all => $(all, [ + map(validateAggregate), + flatten, +]); diff --git a/packages/core/src/transactions/cleanup.js b/packages/core/src/transactions/cleanup.js new file mode 100644 index 0000000000..3c6d1ccc9d --- /dev/null +++ b/packages/core/src/transactions/cleanup.js @@ -0,0 +1,47 @@ +import { map } from 'lodash/fp'; +import { retrieve } from './retrieve'; +import { executeTransactions } from './execute'; +import { + $, joinKey, getLock, isNolock, releaseLock, +} from '../common'; +import { + LOCK_FILE_KEY, TRANSACTIONS_FOLDER, + timeoutMilliseconds, getTransactionId, + maxLockRetries, +} from './transactionsCommon'; + +export const cleanup = async (app) => { + const lock = await getTransactionLock(app); + if (isNolock(lock)) return; + + try { + const transactions = await retrieve(app); + if (transactions.length > 0) { + await executeTransactions(app)(transactions); + + const folder = transactions.folderKey + ? transactions.folderKey + : TRANSACTIONS_FOLDER; + + const deleteFiles = $(transactions, [ + map(t => joinKey( + folder, + getTransactionId( + t.recordId, t.transactionType, + t.uniqueId, + ), + )), + map(app.datastore.deleteFile), + ]); + + await Promise.all(deleteFiles); + } + } finally { + await releaseLock(app, lock); + } +}; + +const getTransactionLock = async app => await getLock( + app, LOCK_FILE_KEY, + timeoutMilliseconds, maxLockRetries, +); diff --git a/packages/core/src/transactions/create.js b/packages/core/src/transactions/create.js new file mode 100644 index 0000000000..0af8b0c2d1 --- /dev/null +++ b/packages/core/src/transactions/create.js @@ -0,0 +1,69 @@ +import { generate } from 'shortid'; +import { joinKey } from '../common'; +import { getLastPartInKey } from '../templateApi/hierarchy'; +import { + IndexNodeKeyFolder, BUILDINDEX_BATCH_COUNT, + IndexNodeKeyBatchFolder, TRANSACTIONS_FOLDER, getTransactionId, CREATE_RECORD_TRANSACTION, UPDATE_RECORD_TRANSACTION, + DELETE_RECORD_TRANSACTION, BUILD_INDEX_TRANSACTION, +} from './transactionsCommon'; + + +export const transactionForCreateRecord = async (app, record) => await transaction( + app.datastore, CREATE_RECORD_TRANSACTION, + record.key, { record }, + getTransactionKey_Records, +); + +export const transactionForUpdateRecord = async (app, oldRecord, newRecord) => await transaction( + app.datastore, UPDATE_RECORD_TRANSACTION, + newRecord.key, { oldRecord, record: newRecord }, + getTransactionKey_Records, +); + +export const transactionForDeleteRecord = async (app, record) => await transaction( + app.datastore, DELETE_RECORD_TRANSACTION, + record.key, { record }, + getTransactionKey_Records, +); + +export const transactionForBuildIndex = async (app, indexNodeKey, recordKey, count) => { + const transactionFolder = IndexNodeKeyBatchFolder(indexNodeKey, count); + if (count % BUILDINDEX_BATCH_COUNT === 0) { + await app.datastore.createFolder(transactionFolder); + } + + return await transaction( + app.datastore, BUILD_INDEX_TRANSACTION, + recordKey, { recordKey }, + id => joinKey(transactionFolder, id), + ); +}; + +export const createBuildIndexFolder = async (datastore, indexNodeKey) => await datastore.createFolder( + IndexNodeKeyFolder(indexNodeKey), +); + +const getTransactionKey_Records = id => joinKey(TRANSACTIONS_FOLDER, id); + +const transaction = async (datastore, transactionType, recordKey, data, getTransactionKey) => { + const recordId = getLastPartInKey(recordKey); + const uniqueId = generate(); + const id = getTransactionId( + recordId, transactionType, uniqueId, + ); + + const key = getTransactionKey(id); + + const trans = { + transactionType, + recordKey, + ...data, + id, + }; + + await datastore.createJson( + key, trans, + ); + + return trans; +}; diff --git a/packages/core/src/transactions/execute.js b/packages/core/src/transactions/execute.js new file mode 100644 index 0000000000..a71a3c6172 --- /dev/null +++ b/packages/core/src/transactions/execute.js @@ -0,0 +1,349 @@ +import { + filter, map, isUndefined, includes, + flatten, intersectionBy, + isEqual, pull, keys, + differenceBy, difference, +} from 'lodash/fp'; +import { union } from 'lodash'; +import { + getRelevantAncestorIndexes, + getRelevantReverseReferenceIndexes, +} from '../indexing/relevant'; +import { evaluate } from '../indexing/evaluate'; +import { + $, isSomething, + isNonEmptyArray, joinKey, + isNonEmptyString, +} from '../common'; +import { getIndexedDataKey } from '../indexing/sharding'; +import { + isUpdate, isCreate, + isDelete, isBuildIndex, +} from './transactionsCommon'; +import { applyToShard } from '../indexing/apply'; +import { + getActualKeyOfParent, + isGlobalIndex, fieldReversesReferenceToIndex, isReferenceIndex, + getExactNodeForPath, +} from '../templateApi/hierarchy'; + +export const executeTransactions = app => async (transactions) => { + const recordsByShard = mappedRecordsByIndexShard(app.hierarchy, transactions); + + for (const shard of keys(recordsByShard)) { + await applyToShard( + app.hierarchy, app.datastore, + recordsByShard[shard].indexKey, + recordsByShard[shard].indexNode, + shard, + recordsByShard[shard].writes, + recordsByShard[shard].removes, + ); + } +}; + +const mappedRecordsByIndexShard = (hierarchy, transactions) => { + const updates = getUpdateTransactionsByShard( + hierarchy, transactions, + ); + + const created = getCreateTransactionsByShard( + hierarchy, transactions, + ); + const deletes = getDeleteTransactionsByShard( + hierarchy, transactions, + ); + + const indexBuild = getBuildIndexTransactionsByShard( + hierarchy, + transactions, + ); + + const toRemove = [ + ...deletes, + ...updates.toRemove, + ]; + + const toWrite = [ + ...created, + ...updates.toWrite, + ...indexBuild, + ]; + + const transByShard = {}; + + const initialiseShard = (t) => { + if (isUndefined(transByShard[t.indexShardKey])) { + transByShard[t.indexShardKey] = { + writes: [], + removes: [], + indexKey: t.indexKey, + indexNodeKey: t.indexNodeKey, + indexNode: t.indexNode, + }; + } + }; + + for (const trans of toWrite) { + initialiseShard(trans); + transByShard[trans.indexShardKey].writes.push( + trans.mappedRecord.result, + ); + } + + for (const trans of toRemove) { + initialiseShard(trans); + transByShard[trans.indexShardKey].removes.push( + trans.mappedRecord.result.key, + ); + } + + return transByShard; +}; + +const getUpdateTransactionsByShard = (hierarchy, transactions) => { + const updateTransactions = $(transactions, [filter(isUpdate)]); + + const evaluateIndex = (record, indexNodeAndPath) => { + const mappedRecord = evaluate(record)(indexNodeAndPath.indexNode); + return ({ + mappedRecord, + indexNode: indexNodeAndPath.indexNode, + indexKey: indexNodeAndPath.indexKey, + indexShardKey: getIndexedDataKey( + indexNodeAndPath.indexNode, + indexNodeAndPath.indexKey, + mappedRecord.result, + ), + }); + }; + + const getIndexNodesToApply = indexFilter => (t, indexes) => $(indexes, [ + map(n => ({ + old: evaluateIndex(t.oldRecord, n), + new: evaluateIndex(t.record, n), + })), + filter(indexFilter), + ]); + + const toRemoveFilter = (n, isUnreferenced) => n.old.mappedRecord.passedFilter === true + && (n.new.mappedRecord.passedFilter === false + || isUnreferenced); + + const toAddFilter = (n, isNewlyReferenced) => (n.old.mappedRecord.passedFilter === false + || isNewlyReferenced) + && n.new.mappedRecord.passedFilter === true; + + const toUpdateFilter = n => n.new.mappedRecord.passedFilter === true + && n.old.mappedRecord.passedFilter === true + && !isEqual(n.old.mappedRecord.result, + n.new.mappedRecord.result); + + const toRemove = []; + const toWrite = []; + + for (const t of updateTransactions) { + const ancestorIdxs = getRelevantAncestorIndexes( + hierarchy, t.record, + ); + + const referenceChanges = diffReverseRefForUpdate( + hierarchy, t.oldRecord, t.record, + ); + + // old records to remove (filtered out) + const filteredOut_toRemove = union( + getIndexNodesToApply(toRemoveFilter)(t, ancestorIdxs), + // still referenced - check filter + getIndexNodesToApply(toRemoveFilter)(t, referenceChanges.notChanged), + // un referenced - remove if in there already + getIndexNodesToApply(n => toRemoveFilter(n, true))(t, referenceChanges.unReferenced), + ); + + // new records to add (filtered in) + const filteredIn_toAdd = union( + getIndexNodesToApply(toAddFilter)(t, ancestorIdxs), + // newly referenced - check filter + getIndexNodesToApply(n => toAddFilter(n, true))(t, referenceChanges.newlyReferenced), + // reference unchanged - rerun filter in case something else changed + getIndexNodesToApply(toAddFilter)(t, referenceChanges.notChanged), + ); + + const changed = union( + getIndexNodesToApply(toUpdateFilter)(t, ancestorIdxs), + // still referenced - recheck filter + getIndexNodesToApply(toUpdateFilter)(t, referenceChanges.notChanged), + ); + + const shardKeyChanged = $(changed, [ + filter(c => c.old.indexShardKey !== c.new.indexShardKey), + ]); + + const changedInSameShard = $(shardKeyChanged, [ + difference(changed), + ]); + + for (const res of shardKeyChanged) { + pull(res)(changed); + filteredOut_toRemove.push(res); + filteredIn_toAdd.push(res); + } + + toRemove.push( + $(filteredOut_toRemove, [ + map(i => i.old), + ]), + ); + + toWrite.push( + $(filteredIn_toAdd, [ + map(i => i.new), + ]), + ); + + toWrite.push( + $(changedInSameShard, [ + map(i => i.new), + ]), + ); + } + + return ({ + toRemove: flatten(toRemove), + toWrite: flatten(toWrite), + }); +}; + +const getBuildIndexTransactionsByShard = (hierarchy, transactions) => { + const buildTransactions = $(transactions, [filter(isBuildIndex)]); + if (!isNonEmptyArray(buildTransactions)) return []; + const indexNode = transactions.indexNode; + + const getIndexKeys = (t) => { + if (isGlobalIndex(indexNode)) { + return [indexNode.nodeKey()]; + } + + if (isReferenceIndex(indexNode)) { + const recordNode = getExactNodeForPath(hierarchy)(t.record.key); + const refFields = $(recordNode.fields, [ + filter(fieldReversesReferenceToIndex(indexNode)), + ]); + const indexKeys = []; + for (const refField of refFields) { + const refValue = t.record[refField.name]; + if (isSomething(refValue) + && isNonEmptyString(refValue.key)) { + const indexKey = joinKey( + refValue.key, + indexNode.name, + ); + + if (!includes(indexKey)(indexKeys)) { indexKeys.push(indexKey); } + } + } + return indexKeys; + } + + return [joinKey( + getActualKeyOfParent( + indexNode.parent().nodeKey(), + t.record.key, + ), + indexNode.name, + )]; + }; + + return $(buildTransactions, [ + map((t) => { + const mappedRecord = evaluate(t.record)(indexNode); + if (!mappedRecord.passedFilter) return null; + const indexKeys = getIndexKeys(t); + return $(indexKeys, [ + map(indexKey => ({ + mappedRecord, + indexNode, + indexKey, + indexShardKey: getIndexedDataKey( + indexNode, + indexKey, + mappedRecord.result, + ), + })), + ]); + }), + flatten, + filter(isSomething), + ]); +}; + +const get_Create_Delete_TransactionsByShard = pred => (hierarchy, transactions) => { + const createTransactions = $(transactions, [filter(pred)]); + + const getIndexNodesToApply = (t, indexes) => $(indexes, [ + map((n) => { + const mappedRecord = evaluate(t.record)(n.indexNode); + return ({ + mappedRecord, + indexNode: n.indexNode, + indexKey: n.indexKey, + indexShardKey: getIndexedDataKey( + n.indexNode, + n.indexKey, + mappedRecord.result, + ), + }); + }), + filter(n => n.mappedRecord.passedFilter), + ]); + + const allToApply = []; + + for (const t of createTransactions) { + const ancestorIdxs = getRelevantAncestorIndexes(hierarchy, t.record); + const reverseRef = getRelevantReverseReferenceIndexes(hierarchy, t.record); + + allToApply.push( + getIndexNodesToApply(t, ancestorIdxs), + ); + allToApply.push( + getIndexNodesToApply(t, reverseRef), + ); + } + + return flatten(allToApply); +}; + +const getDeleteTransactionsByShard = get_Create_Delete_TransactionsByShard(isDelete); + +const getCreateTransactionsByShard = get_Create_Delete_TransactionsByShard(isCreate); + +const diffReverseRefForUpdate = (appHierarchy, oldRecord, newRecord) => { + const oldIndexes = getRelevantReverseReferenceIndexes( + appHierarchy, oldRecord, + ); + const newIndexes = getRelevantReverseReferenceIndexes( + appHierarchy, newRecord, + ); + + const unReferenced = differenceBy( + i => i.indexKey, + oldIndexes, newIndexes, + ); + + const newlyReferenced = differenceBy( + i => i.indexKey, + newIndexes, oldIndexes, + ); + + const notChanged = intersectionBy( + i => i.indexKey, + newIndexes, oldIndexes, + ); + + return { + unReferenced, + newlyReferenced, + notChanged, + }; +}; diff --git a/packages/core/src/transactions/retrieve.js b/packages/core/src/transactions/retrieve.js new file mode 100644 index 0000000000..ca545166f7 --- /dev/null +++ b/packages/core/src/transactions/retrieve.js @@ -0,0 +1,206 @@ +import { + map, filter, groupBy, split, + some, find, +} from 'lodash/fp'; +import { + LOCK_FILENAME, TRANSACTIONS_FOLDER, idSep, isUpdate, + nodeKeyHashFromBuildFolder, isBuildIndexFolder, getTransactionId, + isDelete, isCreate, +} from './transactionsCommon'; +import { + joinKey, $, none, isSomething, +} from '../common'; +import { getLastPartInKey, getNodeFromNodeKeyHash } from '../templateApi/hierarchy'; +import { _load } from '../recordApi/load'; + +export const retrieve = async (app) => { + const transactionFiles = await app.datastore.getFolderContents( + TRANSACTIONS_FOLDER, + ); + + let transactions = []; + + if (some(isBuildIndexFolder)(transactionFiles)) { + const buildIndexFolder = find(isBuildIndexFolder)(transactionFiles); + + transactions = await retrieveBuildIndexTransactions( + app, + joinKey(TRANSACTIONS_FOLDER, buildIndexFolder), + ); + } + + if (transactions.length > 0) return transactions; + + return await retrieveStandardTransactions( + app, transactionFiles, + ); +}; + +const retrieveBuildIndexTransactions = async (app, buildIndexFolder) => { + const childFolders = await app.datastore.getFolderContents(buildIndexFolder); + if (childFolders.length === 0) { + // cleanup + await app.datastore.deleteFolder(buildIndexFolder); + return []; + } + + const getTransactionFiles = async (childFolderIndex = 0) => { + if (childFolderIndex >= childFolders.length) return []; + + const childFolderKey = joinKey(buildIndexFolder, childFolders[childFolderIndex]); + const files = await app.datastore.getFolderContents( + childFolderKey, + ); + + if (files.length === 0) { + await app.datastore.deleteFolder(childFolderKey); + return await getTransactionFiles(childFolderIndex + 1); + } + + return { childFolderKey, files }; + }; + + const transactionFiles = await getTransactionFiles(); + + if (transactionFiles.files.length === 0) return []; + + const transactions = $(transactionFiles.files, [ + map(parseTransactionId), + ]); + + for (const t of transactions) { + const transactionContent = await app.datastore.loadJson( + joinKey( + transactionFiles.childFolderKey, + t.fullId, + ), + ); + t.record = await _load(app, transactionContent.recordKey); + } + + transactions.indexNode = $(buildIndexFolder, [ + getLastPartInKey, + nodeKeyHashFromBuildFolder, + getNodeFromNodeKeyHash(app.hierarchy), + ]); + + transactions.folderKey = transactionFiles.childFolderKey; + + return transactions; +}; + +const retrieveStandardTransactions = async (app, transactionFiles) => { + const transactionIds = $(transactionFiles, [ + filter(f => f !== LOCK_FILENAME + && !isBuildIndexFolder(f)), + map(parseTransactionId), + ]); + + const transactionIdsByRecord = $(transactionIds, [ + groupBy('recordId'), + ]); + + const dedupedTransactions = []; + + const verify = async (t) => { + if (t.verified === true) return t; + + const id = getTransactionId( + t.recordId, + t.transactionType, + t.uniqueId, + ); + + const transaction = await app.datastore.loadJson( + joinKey(TRANSACTIONS_FOLDER, id), + ); + + if (isDelete(t)) { + t.record = transaction.record; + t.verified = true; + return t; + } + + const rec = await _load( + app, + transaction.recordKey, + ); + if (rec.transactionId === id) { + t.record = rec; + if (transaction.oldRecord) { t.oldRecord = transaction.oldRecord; } + t.verified = true; + } else { + t.verified = false; + } + + return t; + }; + + const pickOne = async (trans, forType) => { + const transForType = filter(forType)(trans); + if (transForType.length === 1) { + const t = await verify(transForType[0]); + return (t.verified === true ? t : null); + } + for (let t of transForType) { + t = await verify(t); + if (t.verified === true) { return t; } + } + + return null; + }; + + for (const recordId in transactionIdsByRecord) { + const transIdsForRecord = transactionIdsByRecord[recordId]; + if (transIdsForRecord.length === 1) { + const t = await verify(transIdsForRecord[0]); + if (t.verified) { dedupedTransactions.push(t); } + continue; + } + if (some(isDelete)(transIdsForRecord)) { + const t = await verify(find(isDelete)(transIdsForRecord)); + if (t.verified) { dedupedTransactions.push(t); } + continue; + } + if (some(isUpdate)(transIdsForRecord)) { + const upd = await pickOne(transIdsForRecord, isUpdate); + if (isSomething(upd) && upd.verified) { dedupedTransactions.push(upd); } + continue; + } + if (some(isCreate)(transIdsForRecord)) { + const cre = await pickOne(transIdsForRecord, isCreate); + if (isSomething(cre)) { dedupedTransactions.push(cre); } + continue; + } + } + + const duplicates = $(transactionIds, [ + filter(t => none(ddt => ddt.uniqueId === t.uniqueId)(dedupedTransactions)), + ]); + + + const deletePromises = map(t => app.datastore.deleteFile( + joinKey( + TRANSACTIONS_FOLDER, + getTransactionId( + t.recordId, + t.transactionType, + t.uniqueId, + ), + ), + ))(duplicates); + + await Promise.all(deletePromises); + + return dedupedTransactions; +}; + +const parseTransactionId = (id) => { + const splitId = split(idSep)(id); + return ({ + recordId: splitId[0], + transactionType: splitId[1], + uniqueId: splitId[2], + fullId: id, + }); +}; diff --git a/packages/core/src/transactions/transactionsCommon.js b/packages/core/src/transactions/transactionsCommon.js new file mode 100644 index 0000000000..48d2918421 --- /dev/null +++ b/packages/core/src/transactions/transactionsCommon.js @@ -0,0 +1,48 @@ +import { + joinKey, keySep, getHashCode, +} from '../common'; +import { getLastPartInKey } from '../templateApi/hierarchy'; + +export const TRANSACTIONS_FOLDER = `${keySep}.transactions`; +export const LOCK_FILENAME = 'lock'; +export const LOCK_FILE_KEY = joinKey( + TRANSACTIONS_FOLDER, LOCK_FILENAME, +); +export const idSep = '$'; + +const isOfType = typ => trans => trans.transactionType === typ; + +export const CREATE_RECORD_TRANSACTION = 'create'; +export const UPDATE_RECORD_TRANSACTION = 'update'; +export const DELETE_RECORD_TRANSACTION = 'delete'; +export const BUILD_INDEX_TRANSACTION = 'build'; + +export const isUpdate = isOfType(UPDATE_RECORD_TRANSACTION); +export const isDelete = isOfType(DELETE_RECORD_TRANSACTION); +export const isCreate = isOfType(CREATE_RECORD_TRANSACTION); +export const isBuildIndex = isOfType(BUILD_INDEX_TRANSACTION); + +export const keyToFolderName = nodeKey => getHashCode(nodeKey); + +export const getTransactionId = (recordId, transactionType, uniqueId) => + `${recordId}${idSep}${transactionType}${idSep}${uniqueId}`; + +export const buildIndexFolder = '.BUILD-'; +export const nodeKeyHashFromBuildFolder = folder => folder.replace(buildIndexFolder, ''); + +export const isBuildIndexFolder = key => getLastPartInKey(key).startsWith(buildIndexFolder); + +export const IndexNodeKeyFolder = indexNodeKey => joinKey( + TRANSACTIONS_FOLDER, + buildIndexFolder + keyToFolderName(indexNodeKey), +); + +export const IndexNodeKeyBatchFolder = (indexNodeKey, count) => + joinKey(IndexNodeKeyFolder(indexNodeKey), Math.floor(count / BUILDINDEX_BATCH_COUNT).toString()); + +export const IndexShardKeyFolder = (indexNodeKey, indexShardKey) => + joinKey(IndexNodeKeyFolder(indexNodeKey), indexShardKey); + +export const BUILDINDEX_BATCH_COUNT = 1000; +export const timeoutMilliseconds = 30 * 1000; // 30 secs +export const maxLockRetries = 1; diff --git a/packages/core/src/types/array.js b/packages/core/src/types/array.js new file mode 100644 index 0000000000..d97fd2f8f3 --- /dev/null +++ b/packages/core/src/types/array.js @@ -0,0 +1,59 @@ +import { constant, isArray } from 'lodash'; +import { map } from 'lodash/fp'; +import { + typeFunctions, makerule, + parsedFailed, getDefaultExport, parsedSuccess, +} from './typeHelpers'; +import { + switchCase, defaultCase, toNumberOrNull, + $$, isSafeInteger, +} from '../common'; + +const arrayFunctions = () => typeFunctions({ + default: constant([]), +}); + +const mapToParsedArrary = type => $$( + map(i => type.safeParseValue(i)), + parsedSuccess, +); + +const arrayTryParse = type => switchCase( + [isArray, mapToParsedArrary(type)], + [defaultCase, parsedFailed], +); + +const typeName = type => `array<${type}>`; + + +const options = { + maxLength: { + defaultValue: 10000, + isValid: isSafeInteger, + requirementDescription: 'must be a positive integer', + parse: toNumberOrNull, + }, + minLength: { + defaultValue: 0, + isValid: n => isSafeInteger(n) && n >= 0, + requirementDescription: 'must be a positive integer', + parse: toNumberOrNull, + }, +}; + +const typeConstraints = [ + makerule(async (val, opts) => val === null || val.length >= opts.minLength, + (val, opts) => `must choose ${opts.minLength} or more options`), + makerule(async (val, opts) => val === null || val.length <= opts.maxLength, + (val, opts) => `cannot choose more than ${opts.maxLength} options`), +]; + +export default type => getDefaultExport( + typeName(type.name), + arrayTryParse(type), + arrayFunctions(type), + options, + typeConstraints, + [type.sampleValue], + JSON.stringify, +); diff --git a/packages/core/src/types/bool.js b/packages/core/src/types/bool.js new file mode 100644 index 0000000000..97238bd6af --- /dev/null +++ b/packages/core/src/types/bool.js @@ -0,0 +1,40 @@ +import { constant, isBoolean, isNull } from 'lodash'; +import { + typeFunctions, + makerule, parsedFailed, parsedSuccess, + getDefaultExport, +} from './typeHelpers'; +import { + switchCase, defaultCase, isOneOf, toBoolOrNull, +} from '../common'; + +const boolFunctions = typeFunctions({ + default: constant(null), +}); + +const boolTryParse = switchCase( + [isBoolean, parsedSuccess], + [isNull, parsedSuccess], + [isOneOf('true', '1', 'yes', 'on'), () => parsedSuccess(true)], + [isOneOf('false', '0', 'no', 'off'), () => parsedSuccess(false)], + [defaultCase, parsedFailed], +); + +const options = { + allowNulls: { + defaultValue: true, + isValid: isBoolean, + requirementDescription: 'must be a true or false', + parse: toBoolOrNull, + }, +}; + +const typeConstraints = [ + makerule(async (val, opts) => opts.allowNulls === true || val !== null, + () => 'field cannot be null'), +]; + +export default getDefaultExport( + 'bool', boolTryParse, boolFunctions, + options, typeConstraints, true, JSON.stringify, +); diff --git a/packages/core/src/types/datetime.js b/packages/core/src/types/datetime.js new file mode 100644 index 0000000000..c9dcf1b19b --- /dev/null +++ b/packages/core/src/types/datetime.js @@ -0,0 +1,62 @@ +import { + constant, isDate, isString, isNull, +} from 'lodash'; +import { + makerule, typeFunctions, + parsedFailed, parsedSuccess, getDefaultExport, +} from './typeHelpers'; +import { + switchCase, defaultCase, toDateOrNull, +} from '../common'; + +const dateFunctions = typeFunctions({ + default: constant(null), + now: () => new Date(), +}); + +const isValidDate = d => d instanceof Date && !isNaN(d); + +const parseStringToDate = s => switchCase( + [isValidDate, parsedSuccess], + [defaultCase, parsedFailed], +)(new Date(s)); + + +const dateTryParse = switchCase( + [isDate, parsedSuccess], + [isString, parseStringToDate], + [isNull, parsedSuccess], + [defaultCase, parsedFailed], +); + +const options = { + maxValue: { + defaultValue: new Date(32503680000000), + isValid: isDate, + requirementDescription: 'must be a valid date', + parse: toDateOrNull, + }, + minValue: { + defaultValue: new Date(-8520336000000), + isValid: isDate, + requirementDescription: 'must be a valid date', + parse: toDateOrNull, + }, +}; + +const typeConstraints = [ + makerule(async (val, opts) => val === null || opts.minValue === null || val >= opts.minValue, + (val, opts) => `value (${val.toString()}) must be greater than or equal to ${opts.minValue}`), + makerule(async (val, opts) => val === null || opts.maxValue === null || val <= opts.maxValue, + (val, opts) => `value (${val.toString()}) must be less than or equal to ${opts.minValue} options`), +]; + +export default getDefaultExport( + 'datetime', + dateTryParse, + dateFunctions, + options, + typeConstraints, + new Date(1984, 4, 1), + date => JSON.stringify(date).replace(new RegExp('"', 'g'), ''), +); diff --git a/packages/core/src/types/file.js b/packages/core/src/types/file.js new file mode 100644 index 0000000000..62a4f35272 --- /dev/null +++ b/packages/core/src/types/file.js @@ -0,0 +1,58 @@ +import { + last, has, isString, intersection, + isNull, isNumber, +} from 'lodash/fp'; +import { + typeFunctions, parsedFailed, + parsedSuccess, getDefaultExport, +} from './typeHelpers'; +import { + switchCase, defaultCase, none, + $, splitKey, +} from '../common'; + +const illegalCharacters = '*?\\/:<>|\0\b\f\v'; + +export const isLegalFilename = (filePath) => { + const fn = fileName(filePath); + return fn.length <= 255 + && intersection(fn.split(''))(illegalCharacters.split('')).length === 0 + && none(f => f === '..')(splitKey(filePath)); +}; + +const fileNothing = () => ({ relativePath: '', size: 0 }); + +const fileFunctions = typeFunctions({ + default: fileNothing, +}); + +const fileTryParse = v => switchCase( + [isValidFile, parsedSuccess], + [isNull, () => parsedSuccess(fileNothing())], + [defaultCase, parsedFailed], +)(v); + +const fileName = filePath => $(filePath, [ + splitKey, + last, +]); + +const isValidFile = f => !isNull(f) + && has('relativePath')(f) && has('size')(f) + && isNumber(f.size) + && isString(f.relativePath) + && isLegalFilename(f.relativePath); + +const options = {}; + +const typeConstraints = []; + +export default getDefaultExport( + 'file', + fileTryParse, + fileFunctions, + options, + typeConstraints, + { relativePath: 'some_file.jpg', size: 1000 }, + JSON.stringify, +); diff --git a/packages/core/src/types/index.js b/packages/core/src/types/index.js new file mode 100644 index 0000000000..c704775345 --- /dev/null +++ b/packages/core/src/types/index.js @@ -0,0 +1,75 @@ +import { + assign, keys, merge, has, +} from 'lodash'; +import { + map, isString, isNumber, + isBoolean, isDate, + isObject, isArray, +} from 'lodash/fp'; +import { $ } from '../common'; +import { parsedSuccess } from './typeHelpers'; +import string from './string'; +import bool from './bool'; +import number from './number'; +import datetime from './datetime'; +import array from './array'; +import reference from './reference'; +import file from './file'; +import { BadRequestError } from '../common/errors'; + +const allTypes = () => { + const basicTypes = { + string, number, datetime, bool, reference, file, + }; + + const arrays = $(basicTypes, [ + keys, + map((k) => { + const kvType = {}; + const concreteArray = array(basicTypes[k]); + kvType[concreteArray.name] = concreteArray; + return kvType; + }), + types => assign({}, ...types), + ]); + + return merge({}, basicTypes, arrays); +}; + + +export const all = allTypes(); + +export const getType = (typeName) => { + if (!has(all, typeName)) throw new BadRequestError(`Do not recognise type ${typeName}`); + return all[typeName]; +}; + +export const getSampleFieldValue = field => getType(field.type).sampleValue; + +export const getNewFieldValue = field => getType(field.type).getNew(field); + +export const safeParseField = (field, record) => getType(field.type).safeParseField(field, record); + +export const validateFieldParse = (field, record) => (has(record, field.name) + ? getType(field.type).tryParse(record[field.name]) + : parsedSuccess(undefined)); // fields may be undefined by default + +export const getDefaultOptions = type => getType(type).getDefaultOptions(); + +export const validateTypeConstraints = async (field, record, context) => await getType(field.type).validateTypeConstraints(field, record, context); + +export const detectType = (value) => { + if (isString(value)) return string; + if (isBoolean(value)) return bool; + if (isNumber(value)) return number; + if (isDate(value)) return datetime; + if (isArray(value)) return array(detectType(value[0])); + if (isObject(value) + && has(value, 'key') + && has(value, 'value')) return reference; + if (isObject(value) + && has(value, 'relativePath') + && has(value, 'size')) return file; + + throw new BadRequestError(`cannot determine type: ${JSON.stringify(value)}`); +}; diff --git a/packages/core/src/types/number.js b/packages/core/src/types/number.js new file mode 100644 index 0000000000..22529cf2b9 --- /dev/null +++ b/packages/core/src/types/number.js @@ -0,0 +1,73 @@ +import { + constant, isNumber, isString, isNull, +} from 'lodash'; +import { + makerule, typeFunctions, + parsedFailed, parsedSuccess, getDefaultExport, +} from './typeHelpers'; +import { + switchCase, defaultCase, toNumberOrNull, + isSafeInteger, +} from '../common'; + +const numberFunctions = typeFunctions({ + default: constant(null), +}); + +const parseStringtoNumberOrNull = (s) => { + const num = Number(s); + return isNaN(num) ? parsedFailed(s) : parsedSuccess(num); +}; + +const numberTryParse = switchCase( + [isNumber, parsedSuccess], + [isString, parseStringtoNumberOrNull], + [isNull, parsedSuccess], + [defaultCase, parsedFailed], +); + +const options = { + maxValue: { + defaultValue: Number.MAX_SAFE_INTEGER, + isValid: isSafeInteger, + requirementDescription: 'must be a valid integer', + parse: toNumberOrNull, + }, + minValue: { + defaultValue: 0 - Number.MAX_SAFE_INTEGER, + isValid: isSafeInteger, + requirementDescription: 'must be a valid integer', + parse: toNumberOrNull, + }, + decimalPlaces: { + defaultValue: 0, + isValid: n => isSafeInteger(n) && n >= 0, + requirementDescription: 'must be a positive integer', + parse: toNumberOrNull, + }, +}; + +const getDecimalPlaces = (val) => { + const splitDecimal = val.toString().split('.'); + if (splitDecimal.length === 1) return 0; + return splitDecimal[1].length; +}; + +const typeConstraints = [ + makerule(async (val, opts) => val === null || opts.minValue === null || val >= opts.minValue, + (val, opts) => `value (${val.toString()}) must be greater than or equal to ${opts.minValue}`), + makerule(async (val, opts) => val === null || opts.maxValue === null || val <= opts.maxValue, + (val, opts) => `value (${val.toString()}) must be less than or equal to ${opts.minValue} options`), + makerule(async (val, opts) => val === null || opts.decimalPlaces >= getDecimalPlaces(val), + (val, opts) => `value (${val.toString()}) must have ${opts.decimalPlaces} decimal places or less`), +]; + +export default getDefaultExport( + 'number', + numberTryParse, + numberFunctions, + options, + typeConstraints, + 1, + num => num.toString(), +); diff --git a/packages/core/src/types/object.js b/packages/core/src/types/object.js new file mode 100644 index 0000000000..d961e89a27 --- /dev/null +++ b/packages/core/src/types/object.js @@ -0,0 +1,52 @@ +import { + keys, isObject, has, + clone, map, isNull, constant, +} from 'lodash'; +import { + typeFunctions, parsedFailed, + parsedSuccess, getDefaultExport, +} from './typeHelpers'; +import { switchCase, defaultCase, $ } from '../common'; + +const objectFunctions = (definition, allTypes) => typeFunctions({ + default: constant(null), + initialise: () => $(keys(definition), [ + map(() => { + const defClone = clone(definition); + for (const k in defClone) { + defClone[k] = allTypes[k].getNew(); + } + return defClone; + }), + ]), + +}); + + +const parseObject = (definition, allTypes) => (record) => { + const defClone = clone(definition); + for (const k in defClone) { + const type = allTypes[defClone[k]]; + defClone[k] = has(record, k) + ? type.safeParseValue(record[k]) + : type.getNew(); + } + return parsedSuccess(defClone); +}; + + +const objectTryParse = (definition, allTypes) => switchCase( + [isNull, parsedSuccess], + [isObject, parseObject(definition, allTypes)], + [defaultCase, parsedFailed], +); + +export default (typeName, definition, allTypes, defaultOptions, typeConstraints, sampleValue) => getDefaultExport( + typeName, + objectTryParse(definition, allTypes), + objectFunctions(definition, allTypes), + defaultOptions, + typeConstraints, + sampleValue, + JSON.stringify, +); diff --git a/packages/core/src/types/reference.js b/packages/core/src/types/reference.js new file mode 100644 index 0000000000..59c540a7f9 --- /dev/null +++ b/packages/core/src/types/reference.js @@ -0,0 +1,74 @@ +import { + isString, isObjectLike, + isNull, has, isEmpty, +} from 'lodash'; +import { + typeFunctions, makerule, + parsedSuccess, getDefaultExport, + parsedFailed, +} from './typeHelpers'; +import { + switchCase, defaultCase, + isNonEmptyString, isArrayOfString, +} from '../common'; + +const referenceNothing = () => ({ key: '' }); + +const referenceFunctions = typeFunctions({ + default: referenceNothing, +}); + +const hasStringValue = (ob, path) => has(ob, path) + && isString(ob[path]); + +const isObjectWithKey = v => isObjectLike(v) + && hasStringValue(v, 'key'); + +const referenceTryParse = v => switchCase( + [isObjectWithKey, parsedSuccess], + [isNull, () => parsedSuccess(referenceNothing())], + [defaultCase, parsedFailed], +)(v); + +const options = { + indexNodeKey: { + defaultValue: null, + isValid: isNonEmptyString, + requirementDescription: 'must be a non-empty string', + parse: s => s, + }, + displayValue: { + defaultValue: '', + isValid: isNonEmptyString, + requirementDescription: 'must be a non-empty string', + parse: s => s, + }, + reverseIndexNodeKeys: { + defaultValue: null, + isValid: v => isArrayOfString(v) && v.length > 0, + requirementDescription: 'must be a non-empty array of strings', + parse: s => s, + }, +}; + +const isEmptyString = s => isString(s) && isEmpty(s); + +const ensureReferenceExists = async (val, opts, context) => isEmptyString(val.key) + || await context.referenceExists(opts, val.key); + +const typeConstraints = [ + makerule( + ensureReferenceExists, + (val, opts) => `"${val[opts.displayValue]}" does not exist in options list (key: ${val.key})`, + ), +]; + +export default getDefaultExport( + 'reference', + referenceTryParse, + referenceFunctions, + options, + typeConstraints, + { key: 'key', value: 'value' }, + JSON.stringify, +); diff --git a/packages/core/src/types/string.js b/packages/core/src/types/string.js new file mode 100644 index 0000000000..a766dcdaf0 --- /dev/null +++ b/packages/core/src/types/string.js @@ -0,0 +1,62 @@ +import { + constant, isString, + isNull, includes, isBoolean, +} from 'lodash'; +import { + typeFunctions, + makerule, parsedSuccess, getDefaultExport, +} from './typeHelpers'; +import { + switchCase, defaultCase, toBoolOrNull, toNumberOrNull, + isSafeInteger, isArrayOfString, +} from '../common'; + +const stringFunctions = typeFunctions({ + default: constant(null), +}); + +const stringTryParse = switchCase( + [isString, parsedSuccess], + [isNull, parsedSuccess], + [defaultCase, v => parsedSuccess(v.toString())], +); + +const options = { + maxLength: { + defaultValue: null, + isValid: n => n === null || isSafeInteger(n) && n > 0, + requirementDescription: 'max length must be null (no limit) or a greater than zero integer', + parse: toNumberOrNull, + }, + values: { + defaultValue: null, + isValid: v => v === null || (isArrayOfString(v) && v.length > 0 && v.length < 10000), + requirementDescription: "'values' must be null (no values) or an arry of at least one string", + parse: s => s, + }, + allowDeclaredValuesOnly: { + defaultValue: false, + isValid: isBoolean, + requirementDescription: 'allowDeclaredValuesOnly must be true or false', + parse: toBoolOrNull, + }, +}; + +const typeConstraints = [ + makerule(async (val, opts) => val === null || opts.maxLength === null || val.length <= opts.maxLength, + (val, opts) => `value exceeds maximum length of ${opts.maxLength}`), + makerule(async (val, opts) => val === null + || opts.allowDeclaredValuesOnly === false + || includes(opts.values, val), + (val) => `"${val}" does not exist in the list of allowed values`), +]; + +export default getDefaultExport( + 'string', + stringTryParse, + stringFunctions, + options, + typeConstraints, + 'abcde', + str => str, +); diff --git a/packages/core/src/types/typeHelpers.js b/packages/core/src/types/typeHelpers.js new file mode 100644 index 0000000000..ae5d509d26 --- /dev/null +++ b/packages/core/src/types/typeHelpers.js @@ -0,0 +1,71 @@ +import { merge, has } from 'lodash'; +import { + constant, isUndefined, + mapValues, cloneDeep, +} from 'lodash/fp'; +import { isNotEmpty } from '../common'; + +export const getSafeFieldParser = (tryParse, defaultValueFunctions) => (field, record) => { + if (has(record, field.name)) { + return getSafeValueParser(tryParse, defaultValueFunctions)(record[field.name]); + } + return defaultValueFunctions[field.getUndefinedValue](); +}; + +export const getSafeValueParser = (tryParse, defaultValueFunctions) => (value) => { + const parsed = tryParse(value); + if (parsed.success) { + return parsed.value; + } + return defaultValueFunctions.default(); +}; + +export const getNewValue = (tryParse, defaultValueFunctions) => (field) => { + const getInitialValue = isUndefined(field) || isUndefined(field.getInitialValue) + ? 'default' + : field.getInitialValue; + + return has(defaultValueFunctions, getInitialValue) + ? defaultValueFunctions[getInitialValue]() + : getSafeValueParser(tryParse, defaultValueFunctions)(getInitialValue); +}; + +export const typeFunctions = specificFunctions => merge({ + value: constant, + null: constant(null), +}, specificFunctions); + +export const validateTypeConstraints = validationRules => async (field, record, context) => { + const fieldValue = record[field.name]; + const validateRule = async r => (!await r.isValid(fieldValue, field.typeOptions, context) + ? r.getMessage(fieldValue, field.typeOptions) + : ''); + + const errors = []; + for (const r of validationRules) { + const err = await validateRule(r); + if (isNotEmpty(err)) errors.push(err); + } + + return errors; +}; + +const getDefaultOptions = mapValues(v => v.defaultValue); + +export const makerule = (isValid, getMessage) => ({ isValid, getMessage }); +export const parsedFailed = val => ({ success: false, value: val }); +export const parsedSuccess = val => ({ success: true, value: val }); +export const getDefaultExport = (name, tryParse, functions, options, validationRules, sampleValue, stringify) => ({ + getNew: getNewValue(tryParse, functions), + safeParseField: getSafeFieldParser(tryParse, functions), + safeParseValue: getSafeValueParser(tryParse, functions), + tryParse, + name, + getDefaultOptions: () => getDefaultOptions(cloneDeep(options)), + optionDefinitions: options, + validateTypeConstraints: validateTypeConstraints(validationRules), + sampleValue, + stringify: val => (val === null || val === undefined + ? '' : stringify(val)), + getDefaultValue: functions.default, +}); diff --git a/packages/core/test/actionsApi.execute.spec.js b/packages/core/test/actionsApi.execute.spec.js new file mode 100644 index 0000000000..00d65a994d --- /dev/null +++ b/packages/core/test/actionsApi.execute.spec.js @@ -0,0 +1,95 @@ +import {createAppDefinitionWithActionsAndTriggers} from "./specHelpers"; +import {getAppApis} from "../src"; +import {permission} from "../src/authApi/permissions"; + +describe("actions execute", () => { + + it("should successfully execute actions", async () => { + const {emails, app} = await createAppDefinitionWithActionsAndTriggers(); + + app.actions.sendEmail("an email"); + expect(emails).toEqual(["an email"]); + }); + + it("should successfully execute actions via actions api", async () => { + const {emails, actionsApi} = await createAppDefinitionWithActionsAndTriggers(); + + actionsApi.execute("sendEmail", "an email"); + expect(emails).toEqual(["an email"]); + }); + + it("should throw error when user user does not have permission", async () => { + const {actionsApi, app} = await createAppDefinitionWithActionsAndTriggers(); + app.removePermission(permission.executeAction.get("sendEmail")); + expect(() => actionsApi.execute("sendEmail")).toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {actionsApi, app} = await createAppDefinitionWithActionsAndTriggers(); + app.withOnlyThisPermission(permission.executeAction.get("sendEmail")); + actionsApi.execute("sendEmail", "am email"); + }); + +}); + +describe("triggers execute", () => { + + it("should run action when no condition is set", async () => { + const {logs, + templateApi, behaviourSources} = await createAppDefinitionWithActionsAndTriggers(); + + const {recordApi, withFullAccess} = await getAppApis( + templateApi._storeHandle, behaviourSources); + withFullAccess(); + // trigger setup to add to logs on recordApi:save:onError event + const customer = recordApi.getNew("/customers", "customer"); + + let errCaught; + try { + await recordApi.save(customer); + } catch (e) { + errCaught = e.message; + } + + expect(logs).toEqual([errCaught]); + + }); + + it("should run action when condition is met", async () => { + const {call_timers, + templateApi, + behaviourSources} = await createAppDefinitionWithActionsAndTriggers(); + + const {recordApi, withFullAccess} = await getAppApis( + templateApi._storeHandle, behaviourSources); + withFullAccess(); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + + // trigger call_timer set to return 999 all the time, just for test + // trigger set to run for type = customer + await recordApi.save(customer); + + expect(call_timers).toEqual([999]); + }); + + it("should not run action when condition is not met", async () => { + const {call_timers, + templateApi, + behaviourSources} = await createAppDefinitionWithActionsAndTriggers(); + + const {recordApi, withFullAccess} = await getAppApis( + templateApi._storeHandle, behaviourSources); + withFullAccess(); + + const partner = recordApi.getNew("/partners", "partner"); + + // trigger call_timer set to return 999 all the time, just for test + // trigger set to run for type = customer + await recordApi.save(partner); + + expect(call_timers).toEqual([]); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/apiWrapper.spec.js b/packages/core/test/apiWrapper.spec.js new file mode 100644 index 0000000000..b14cfa47f7 --- /dev/null +++ b/packages/core/test/apiWrapper.spec.js @@ -0,0 +1,166 @@ +import {apiWrapper, apiWrapperSync} from "../src/common/apiWrapper"; +import {filter} from "lodash/fp"; +import {event, onComplete, + onBegin, onError, events} from "../src/common"; + +const getApp = () => { + + var events = []; + + return { + publish: (name, context) => + events.push({name, context}), + events, + getEvents: n => filter(e => e.name === n) + (events) + }; +}; + +describe("apiWrapper", () => { + + const testNamespace = { + onBegin:"testArea:testMethod:onBegin", + onComplete:"testArea:testMethod:onComplete", + onError:"testArea:testMethod:onError", + } + + const getCompleteEvents = app => + app.getEvents(testNamespace.onComplete); + + const getBeginEvents = app => + app.getEvents(testNamespace.onBegin); + + const getErrorEvents = app => + app.getEvents(testNamespace.onError); + + const runThrowEx = (arg1, arg2) => { + const throwEx = (x,y) => {throw new Error("test error");} + const app = getApp(); + try { + apiWrapperSync( + app, + testNamespace, + () => true, + {prop:"hello"}, + throwEx, arg1, arg2 + ); + } catch(error) { + return {app, error}; + } + return {app, error:"error was not thrown"}; + } + + const runThrowExAsync = async (arg1, arg2) => { + const throwEx = async (x,y) => {throw new Error("test error");} + const app = getApp(); + try { + await apiWrapper( + app, + testNamespace, + () => true, + {prop:"hello"}, + throwEx, arg1, arg2 + ); + } catch(error) { + return {app, error}; + } + return {app, error:"error was not thrown"}; + } + + const runAdd = (arg1, arg2, isAuthorized=true) => { + const add = (x,y) => x+y; + const app =getApp(); + const result = apiWrapperSync( + app, + testNamespace, + () => isAuthorized, + {prop:"hello"}, + add, arg1, arg2 + ); + return {app, result}; + } + + const runAddAsync = async (arg1, arg2, isAuthorized=true) => { + const add = async (x,y) => x+y; + const app =getApp(); + const result = await apiWrapper( + app, + testNamespace, + () => isAuthorized, + {prop:"hello"}, + add, arg1, arg2 + ); + return {app, result}; + } + + it("should return result of inner function", () => { + expect(runAdd(1,2).result).toBe(3); + }); + + it("should throw error when unauthorized", () => { + expect(() => runAdd(1,2,false)).toThrow(/Unauthorized/); + }) + + it("should async throw error when unauthorized", async () => { + let foundE = null; + try { + await runAddAsync(1,2,false) + } catch(ex) { + foundE = ex; + } + expect(foundE).not.toBeNull(); + }) + + it("should return result of inner function when async", async () => { + expect( + (await runAddAsync(1,2)).result).toBe(3); + }); + + it("should publish begin and complete events", () => { + const {app} = runAdd(1,2); + const onCompleteEvents = getCompleteEvents(app); + const onBeginEvents = getBeginEvents(app); + expect(onCompleteEvents.length).toBe(1); + expect(onCompleteEvents[0].context.prop).toBe("hello"); + expect(onBeginEvents.length).toBe(1); + expect(onBeginEvents[0].context.prop).toBe("hello"); + }); + + it("should throw exception when inner function happens", () => { + const {error} = runThrowEx(1,2); + expect(error).toBeDefined(); + expect(error.message).toBe("test error"); + }); + + it("should publish error event when inner exception", () => { + const {app} = runThrowEx(1,2); + const errorEvents = getErrorEvents(app); + expect(errorEvents.length).toBe(1); + expect(errorEvents[0].context.error.message).toBe("test error"); + }); + + it("should throw exception when inner function happens, on async", async () => { + const {error} = await runThrowExAsync(1,2); + expect(error).toBeDefined(); + expect(error.message).toBe("test error"); + }); + + it("should publish error event when inner exception, on async", async () => { + const {app} = await runThrowExAsync(1,2); + const errorEvents = getErrorEvents(app); + expect(errorEvents.length).toBe(1); + expect(errorEvents[0].context.error.message).toBe("test error"); + }); + +}); + +describe("events", () => { + it("should return contain various spot checked events", () => { + expect(events.recordApi.save.onComplete) + .toBe("recordApi:save:onComplete"); + expect(events.indexApi.buildIndex.onBegin) + .toBe("indexApi:buildIndex:onBegin"); + expect(events.collectionApi.initialise.onError) + .toBe("collectionApi:initialise:onError"); + }); +}) \ No newline at end of file diff --git a/packages/core/test/authApi.authenticate.spec.js b/packages/core/test/authApi.authenticate.spec.js new file mode 100644 index 0000000000..63af954553 --- /dev/null +++ b/packages/core/test/authApi.authenticate.spec.js @@ -0,0 +1,127 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { permissionTypes, + userAuthFile} from "../src/authApi/authCommon"; +import {permission} from "../src/authApi/permissions"; + + +describe("authApi > authenticate", () => { + + it("should return user + access when correct password supplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "password"); + const result = await authApi.authenticate(u.name, "password"); + expect(result).not.toBeNull(); + expect(result.name).toBe("bob"); + expect(result.temp).toBe(false); + expect(result.passwordHash).toBeUndefined(); + expect(result.temporaryAccessId).toBeUndefined(); + expect(result.permissions.length).toBe(1); + expect(result.permissions[0]).toEqual({type:permissionTypes.SET_PASSWORD}); + }); + + it("should return null when password incorrect", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "password"); + const result = await authApi.authenticate(u.name, "letmein"); + expect(result).toBeNull(); + }); + + it("should return null when non existing user", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const result = await authApi.authenticate("nobody", "password"); + expect(result).toBeNull(); + }); + + it("should return null when user not enabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "password", false); + const result = await authApi.authenticate(u.name, "password"); + expect(result).toBeNull(); + }); + + it("should return null when password not set", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "", false); + const result = await authApi.authenticate(u.name, ""); + expect(result).toBeNull(); + }); + + it("authenticate should be allowed wit no permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withNoPermissions(); + await authApi.authenticate("", ""); + }); + +}); + +describe("authApi > authenticateTemporaryAccess", () => { + + it("should return user with no permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, ""); + const result = await authApi.authenticateTemporaryAccess(u.tempCode); + expect(result).not.toBeNull(); + expect(result.name).toBe("bob"); + expect(result.passwordHash).toBeUndefined(); + expect(result.permissions.length).toBe(0); + expect(result.temp).toBe(true); + }); + + it("should return null when blank code suplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const result = await authApi.authenticateTemporaryAccess(""); + expect(result).toBeNull(); + }); + + it("should return null when invalid code supplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const result = await authApi.authenticateTemporaryAccess("incorrect"); + expect(result).toBeNull(); + }); + + it("should return null when user disabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "", false); + const result = await authApi.authenticateTemporaryAccess(u.tempCode); + expect(result).toBeNull(); + }); + + it("should return null when temporary access code is expired", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, ""); + const userAuth = await app.datastore.loadJson( + userAuthFile(u.name) + ); + userAuth.temporaryAccessExpiryEpoch = 0; + await app.datastore.updateJson( + userAuthFile(u.name), userAuth + ); + const result = await authApi.authenticateTemporaryAccess(u.tempCode); + expect(result).toBeNull(); + }); + + + it("authenticate should be allowed wit no permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withNoPermissions(); + await authApi.authenticateTemporaryAccess(""); + }); + +}); + +const validUser = async (app, authApi, password, enabled=true) => { + const access = await authApi.getNewAccessLevel(app); + access.name = "admin"; + permission.setPassword.add(access); + + await authApi.saveAccessLevels({version:0, levels:[access]}); + + const u = authApi.getNewUser(app); + u.name = "bob"; + u.accessLevels = ["admin"]; + u.enabled = enabled; + + await authApi.createUser(u, password); + return u; +}; \ No newline at end of file diff --git a/packages/core/test/authApi.changePassword.spec.js b/packages/core/test/authApi.changePassword.spec.js new file mode 100644 index 0000000000..325acc5223 --- /dev/null +++ b/packages/core/test/authApi.changePassword.spec.js @@ -0,0 +1,123 @@ +import {setupApphierarchy, validUser, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { parseTemporaryCode, + userAuthFile, + USERS_LIST_FILE, + getUserByName} from "../src/authApi/authCommon"; + + +describe("authApi > changeMyPassword", () => { + + it("should be able to authenticate after a change", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + const firstPasswordCheck = await authApi.authenticate(u.name, "firstpassword"); + expect(firstPasswordCheck).not.toBeNull(); + const changeResult = await authApi.changeMyPassword("firstpassword", "secondpassword"); + expect(changeResult).toBe(true); + const firstPasswordReCheck = await authApi.authenticate(u.name, "firstpassword"); + expect(firstPasswordReCheck).toBeNull(); + const secondPasswordCheck = await authApi.authenticate(u.name, "secondpassword"); + expect(secondPasswordCheck).not.toBeNull(); + }); + + it("should not change password if current password is incorrect", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + const changeResult = await authApi.changeMyPassword("not-firstpassword", "secondpassword"); + expect(changeResult).toBe(false); + const secondPasswordCheck = await authApi.authenticate(u.name, "secondpassword"); + expect(secondPasswordCheck).toBeNull(); + }); + + it("should be allowed with no permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + app.withNoPermissions(); + await authApi.changeMyPassword("firstpassword", "secondpassword"); + }); + +}); + + +describe("authApi > resetPasswordFlow", () => { + + it("should successfully set password from temporary access", async () => { + const {authApi,app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app,authApi, "firstpassword"); + + const tempCode = await authApi.createTemporaryAccess(u.name); + + const result = await authApi.setPasswordFromTemporaryCode(tempCode,"secondpassword"); + expect(result).toBe(true); + const secondPasswordCheck = await authApi.authenticate(u.name, "secondpassword"); + expect(secondPasswordCheck).not.toBeNull(); + + }); + + it("should not set password when temporary access expired", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + + const tempCode = await authApi.createTemporaryAccess(u.name); + + const userAuth = await app.datastore.loadJson( + userAuthFile(u.name) + ); + userAuth.temporaryAccessExpiryEpoch = 0; + await app.datastore.updateJson( + userAuthFile(u.name), userAuth + ); + const result = await authApi.setPasswordFromTemporaryCode(tempCode,"secondpassword"); + expect(result).toBe(false); + const secondPasswordCheck = await authApi.authenticate(u.name, "secondpassword"); + expect(secondPasswordCheck).toBeNull(); + + }); + + it("should still be able to authenticate with password when temp access is set", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + + await authApi.createTemporaryAccess(u.name); + + const secondPasswordCheck = await authApi.authenticate(u.name, "firstpassword"); + expect(secondPasswordCheck).not.toBeNull(); + + }); + +}); + +describe("authApi > createTemporaryAccess", () => { + + it("should set users accessId annd userAuth hash and expiry", async () => { + + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + + const tempCode = await authApi.createTemporaryAccess(u.name); + const tempInfo = parseTemporaryCode(tempCode); + + const userAuth = await app.datastore.loadJson( + userAuthFile(u.name) + ); + + const currentTime = await app.getEpochTime(); + expect(app.crypto.verify(userAuth.temporaryAccessHash, tempInfo.code)).toBeTruthy(); + expect(userAuth.temporaryAccessExpiryEpoch).toBeGreaterThan(currentTime); + + const users = await app.datastore.loadJson(USERS_LIST_FILE); + const user = getUserByName(users, u.name); + + expect(user.temporaryAccessId).toBe(tempInfo.id); + + }); + + it("should be allowed with no permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword"); + app.withNoPermissions(); + await authApi.createTemporaryAccess(u.name); + }); + +}); diff --git a/packages/core/test/authApi.createAccessLevels.spec.js b/packages/core/test/authApi.createAccessLevels.spec.js new file mode 100644 index 0000000000..d7890730ff --- /dev/null +++ b/packages/core/test/authApi.createAccessLevels.spec.js @@ -0,0 +1,190 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { permissionTypes, + ACCESS_LEVELS_FILE, + ACCESS_LEVELS_LOCK_FILE} from "../src/authApi/authCommon"; +import {permission} from "../src/authApi/permissions"; +import {cloneDeep} from "lodash/fp"; +import {getLock} from "../src/common/lock"; + +describe("getNewAccessLevel", () => { + + it("should create item with correct properties", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accLev = authApi.getNewAccessLevel(); + expect(accLev.name).toBe(""); + expect(accLev.permissions).toEqual([]); + }); + +}); + +describe("validateAccessLevels", () => { + + it("should return no errors with valid access level", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accessLevel = validAccessLevel(authApi); + const errs = authApi.validateAccessLevels([accessLevel]); + expect(errs).toEqual([]); + }); + + it("should error when access level name not set", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accessLevel = validAccessLevel(authApi); + accessLevel.name = ""; + const errs = authApi.validateAccessLevels([accessLevel]); + expect(errs.length).toEqual(1); + expect(errs[0].field).toBe("name"); + }); + + it("should error when 2 access levels with the same name", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accessLevel1 = validAccessLevel(authApi); + const accessLevel2 = validAccessLevel(authApi); + const errs = authApi.validateAccessLevels([accessLevel1, accessLevel2]); + expect(errs.length).toEqual(2); + expect(errs[0].field).toBe("name"); + expect(errs[0].item).toBe(accessLevel1); + expect(errs[1].field).toBe("name"); + expect(errs[1].item).toBe(accessLevel2); + }); + + it("should error when permission is not recognised", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accessLevel = validAccessLevel(authApi); + accessLevel.permissions[0].type = "not valid"; + const errs = authApi.validateAccessLevels([accessLevel]); + expect(errs.length).toEqual(1); + expect(errs[0].field).toBe("type"); + expect(errs[0].item).toBe(accessLevel.permissions[0]); + }); + + it("should error when record permision has invalid nodeKey", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const accessLevel = validAccessLevel(authApi); + accessLevel.permissions[0].type = permissionTypes.CREATE_RECORD; + accessLevel.permissions[0].nodeKey = "nota a valid node key"; + const errs = authApi.validateAccessLevels([accessLevel]); + expect(errs.length).toEqual(1); + expect(errs[0].field).toBe("nodeKey"); + expect(errs[0].item).toBe(accessLevel.permissions[0]); + }); +}); + +describe("save and load access level", () => { + + it("should save and load valid access levels", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + await authApi.saveAccessLevels(levels); + const loadedLevels = await authApi.loadAccessLevels(); + expect(loadedLevels.levels.length).toBe(2); + expect(loadedLevels.levels[0].name).toBe("level 1"); + expect(loadedLevels.levels[1].name).toBe("level 2"); + expect(loadedLevels.version).toBe(1); + }); + + it("should not save invalid access levels", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + levels.levels[0].name = ""; + let e; + try { + await authApi.saveAccessLevels(levels); + } catch(ex) { + e = ex; + } + + expect(e).toBeDefined(); + const loadedLevels = await authApi.loadAccessLevels(); + expect(loadedLevels.levels.length).toBe(0); + expect(loadedLevels.version).toBe(0); + }); + + it("should not save access level when version has increased since loading", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + const updatedLevels = cloneDeep(levels); + updatedLevels.version = 1; + await app.datastore.updateJson(ACCESS_LEVELS_FILE, updatedLevels); + + let e; + try { + await authApi.saveAccessLevels(levels); + } catch(ex) { + e = ex; + } + + expect(e).toBeDefined(); + const loadedLevels = await authApi.loadAccessLevels(); + expect(loadedLevels.levels.length).toBe(2); + expect(loadedLevels.version).toBe(1); + }); + + it("should not save access level when locked", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + + await getLock(app, + ACCESS_LEVELS_LOCK_FILE, + 10000, 0,0); + + let e; + try { + await authApi.saveAccessLevels(levels); + } catch(ex) { + e = ex; + } + + expect(e).toBeDefined(); + const loadedLevels = await authApi.loadAccessLevels(); + expect(loadedLevels.levels.length).toBe(0); + expect(loadedLevels.version).toBe(0); + }); + + + it("save should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + app.removePermission(permission.writeAccessLevels.get()); + expect(authApi.saveAccessLevels(levels)).rejects.toThrow(/Unauthorized/); + }); + + it("save should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + app.withOnlyThisPermission(permission.writeAccessLevels.get()); + await authApi.saveAccessLevels(levels) + }); + + it("load should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + await authApi.saveAccessLevels(levels) + app.removePermission(permission.listAccessLevels.get()); + expect(authApi.loadAccessLevels()).rejects.toThrow(/Unauthorized/); + }); + + it("load should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const levels = validAccessLevels(authApi); + await authApi.saveAccessLevels(levels) + app.withOnlyThisPermission(permission.listAccessLevels.get()); + await authApi.loadAccessLevels(); + }); + +}); + +const validAccessLevels = (authApi) => { + const accessLevel1 = validAccessLevel(authApi); + accessLevel1.name = "level 1"; + const accessLevel2 = validAccessLevel(authApi); + accessLevel2.name = "level 2"; + return {version:0, levels: [accessLevel1, accessLevel2]}; +} + +const validAccessLevel = (authApi) => { + const lev = authApi.getNewAccessLevel(); + lev.name = "test level"; + permission.writeTemplates.add(lev); + return lev; +} \ No newline at end of file diff --git a/packages/core/test/authApi.createUser.spec.js b/packages/core/test/authApi.createUser.spec.js new file mode 100644 index 0000000000..5076134c44 --- /dev/null +++ b/packages/core/test/authApi.createUser.spec.js @@ -0,0 +1,190 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { userAuthFile, + USERS_LOCK_FILE} from "../src/authApi/authCommon"; +import {getLock} from "../src/common/lock"; +import {getNewUserAuth} from "../src/authApi/getNewUser"; +import {permission} from "../src/authApi/permissions"; + +describe("getNewUser", () => { + it("should create correct fields", async () => { + const {authApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = authApi.getNewUser(); + expect(user.name).toBe(""); + expect(user.accessLevels).toEqual([]); + expect(user.enabled).toBe(true); + expect(user.temporaryAccessId).toBe(""); + }); +}) + +describe("getNewUser", () => { + it("should create correct fields", async () => { + const {app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const userAuth = getNewUserAuth(app)(); + expect(userAuth.passwordHash).toBe(""); + expect(userAuth.temporaryAccessHash).toEqual(""); + expect(userAuth.temporaryAccessExpiryEpoch).toBe(0); + }); +}); + +describe("validateUsers", () => { + + it("should not return errors for valid user", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + const errs = authApi.validateUser([user], user); + expect(errs).toEqual([]); + }); + + it("should have error when username is not set", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + user.name = ""; + const errs = authApi.validateUser([user], user); + expect(errs.length).toBe(1); + expect(errs[0].field).toBe("name"); + }); + + it("should have error when duplicate usernames", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user1 = validUser(app, authApi); + const user2 = validUser(app, authApi); + const errs = authApi.validateUser([user1, user2], user1); + expect(errs.length).toBe(1); + expect(errs[0].field).toBe("name"); + }); + + it("should have error when no access levels", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + user.accessLevels = []; + const errs = authApi.validateUser([user], user); + expect(errs.length).toBe(1); + expect(errs[0].field).toBe("accessLevels"); + }); + +}); + +describe("create and list users", () => { + + it("should create and load a valid user", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + await authApi.createUser(user); + const users = await authApi.getUsers(); + expect(users.length).toBe(1); + expect(users[0].name).toBe(user.name); + }); + + it("should not save an invalid user", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + user.name = ""; + let e; + try { + await authApi.createUser(user); + } catch(ex) { + e=ex; + } + expect(e).toBeDefined(); + const users = await authApi.getUsers(); + expect(users.length).toBe(0); + }); + + it("should not save when users file is locked", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + await getLock( + app, USERS_LOCK_FILE, 10000, + 0,0); + let e; + try { + await authApi.createUser(user); + } catch(ex) { + e=ex; + } + expect(e).toBeDefined(); + const users = await authApi.getUsers(); + expect(users.length).toBe(0); + }); + + it("should create temporary access when no password supplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + const returnedUser = await authApi.createUser(user); + expect(returnedUser.tempCode.length).toBeGreaterThan(0); + expect(returnedUser.temporaryAccessId.length).toBeGreaterThan(0); + }); + + it("should not store tempCode when temp access created", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + await authApi.createUser(user); + const storedUser = (await authApi.getUsers())[0]; + expect(storedUser.tempCode).toBeUndefined(); + }); + + it("should create user auth file with password hash, when password supplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + const returnedUser = await authApi.createUser(user, "password"); + expect(returnedUser.tempCode).toBeUndefined(); + expect(returnedUser.temporaryAccessId).toBeUndefined(); + + const userAuth = await app.datastore.loadJson( + userAuthFile(user.name) + ); + expect(userAuth.passwordHash.length).toBeGreaterThan(0); + }); + + it("should not create user when user with same name already exists", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + await authApi.createUser(user); + + let e; + try { + await authApi.createUser(user); + } catch(ex) { + e=ex; + } + expect(e).toBeDefined(); + const users = await authApi.getUsers(); + expect(users.length).toBe(1); + }); + + it("create should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + app.removePermission(permission.createUser.get()); + expect(authApi.createUser(user)).rejects.toThrow(/Unauthorized/); + }); + + it("create should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const user = validUser(app, authApi); + app.withOnlyThisPermission(permission.createUser.get()); + await authApi.createUser(user); + }); + + it("list should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.removePermission(permission.listUsers.get()); + expect(authApi.getUsers()).rejects.toThrow(/Unauthorized/); + }); + + it("list should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withOnlyThisPermission(permission.listUsers.get()); + await authApi.getUsers(); + }); + +}); + +const validUser = (app, authApi) => { + const u = authApi.getNewUser(app); + u.name = "bob"; + u.accessLevels = ["admin"]; + u.enabled = true; + return u; +}; \ No newline at end of file diff --git a/packages/core/test/authApi.disableUser.spec.js b/packages/core/test/authApi.disableUser.spec.js new file mode 100644 index 0000000000..39961e3930 --- /dev/null +++ b/packages/core/test/authApi.disableUser.spec.js @@ -0,0 +1,134 @@ +import {setupApphierarchy, validUser, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { parseTemporaryCode, + USERS_LOCK_FILE, + USERS_LIST_FILE, + getUserByName} from "../src/authApi/authCommon"; +import {$} from "../src/common"; +import {getLock} from "../src/common/lock"; +import {permission} from "../src/authApi/permissions"; + +describe("authApi > enableUser", () => { + + it("should enable a user when disabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + await authApi.enableUser(u.name); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.enabled).toBe(true); + + }); + + it("should do nothing when user already enabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", true); + await authApi.enableUser(u.name); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.enabled).toBe(true); + }); + + it("should throw en error when user does not exist", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + let ex; + try { + await authApi.enableUser("nobody"); + } catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should throw en error when users file is locked", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + await getLock(app, USERS_LOCK_FILE, 10000, 0, 0); + let ex; + try { + await authApi.enableUser(u.name); + } catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.removePermission(permission.enableDisableUser.get()); + expect(authApi.enableUser(u)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.withOnlyThisPermission(permission.enableDisableUser.get()); + await authApi.enableUser(u.name); + }); + +}); + +describe("authApi > disableUser", () => { + + it("should disable a user when enabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", true); + await authApi.disableUser(u.name); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.enabled).toBe(false); + + }); + + it("should do nothing when user already enabled", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + await authApi.disableUser(u.name); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.enabled).toBe(false); + }); + + it("should throw en error when user does not exist", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + let ex; + try { + await authApi.disableUser("nobody"); + } catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should throw en error when users file is locked", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + await getLock(app, USERS_LOCK_FILE, 10000, 0, 0); + let ex; + try { + await authApi.disableUser(u.name); + } catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.removePermission(permission.enableDisableUser.get()); + expect(authApi.disableUser(u)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.withOnlyThisPermission(permission.enableDisableUser.get()); + await authApi.disableUser(u.name); + }); + +}); + +const getUser = async (app, authApi, userName) => + $(await app.datastore.loadJson(USERS_LIST_FILE), [ + users => getUserByName(users, userName) + ]); \ No newline at end of file diff --git a/packages/core/test/authApi.setUserAccesslLevels.spec.js b/packages/core/test/authApi.setUserAccesslLevels.spec.js new file mode 100644 index 0000000000..8cbd3d073b --- /dev/null +++ b/packages/core/test/authApi.setUserAccesslLevels.spec.js @@ -0,0 +1,68 @@ +import {setupApphierarchy, validUser, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import { parseTemporaryCode, + USERS_LOCK_FILE, + USERS_LIST_FILE, + getUserByName} from "../src/authApi/authCommon"; +import {$} from "../src/common"; +import {getLock} from "../src/common/lock"; +import {permission} from "../src/authApi/permissions"; + +describe("authApi > enableUser", () => { + + it("should set access levels when valid levels supplied", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", true); + await authApi.setUserAccessLevels(u.name, ["admin2"]); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.accessLevels).toEqual(["admin2"]); + + }); + + it("should throw error when access level invalid", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", true); + expect(authApi.setUserAccessLevels(u.name, ["not valid"])).rejects.toThrow(); + const loadedUser = await getUser(app, authApi, u.name); + expect(loadedUser.accessLevels).toEqual(["admin"]); + }); + + it("should throw en error when user does not exist", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", true); + expect(authApi.setUserAccessLevels("nouser", ["admin"])).rejects.toThrow(); + }); + + it("should throw en error when users file is locked", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + await getLock(app, USERS_LOCK_FILE, 10000, 0, 0); + let ex; + try { + await authApi.setUserAccessLevels(u.name, ["admin"]); + } catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should throw error when user user does not have permission", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.removePermission(permission.setUserAccessLevels.get()); + expect(authApi.setUserAccessLevels(u.name, ["admin"])).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {authApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const u = await validUser(app, authApi, "firstpassword", false); + app.withOnlyThisPermission(permission.setUserAccessLevels.get()); + await authApi.setUserAccessLevels(u.name, ["admin"]); + }); + +}); + +const getUser = async (app, authApi, userName) => + $(await app.datastore.loadJson(USERS_LIST_FILE), [ + users => getUserByName(users, userName) + ]); \ No newline at end of file diff --git a/packages/core/test/collectionApi.delete.spec.js b/packages/core/test/collectionApi.delete.spec.js new file mode 100644 index 0000000000..64ae14de4f --- /dev/null +++ b/packages/core/test/collectionApi.delete.spec.js @@ -0,0 +1,71 @@ +import {setupApphierarchy, basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import {$, splitKey} from "../src/common"; +import {keys, filter} from "lodash/fp"; +import {permission} from "../src/authApi/permissions"; + +describe("collectionApi > delete", () => { + + it("should remove every key in collection's path", async () => { + const {recordApi, collectionApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const childRecord = recordApi.getNew(`${record1.key}/invoices`, "invoice"); + + await recordApi.save(childRecord); + + await collectionApi.delete("/customers"); + + const remainingKeys = $(recordApi._storeHandle.data, [ + keys, + filter(k => splitKey(k)[0] === "customers") + ]); + + expect(remainingKeys).toEqual([]); + + }); + + it("should not delete anything that is not in its path", async () => { + const {recordApi, collectionApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + + await recordApi.save(customer); + + const partner = recordApi.getNew("/partners", "partner"); + await recordApi.save(partner); + + const allKeys = keys(recordApi._storeHandle.data); + const customerKeys = $(allKeys, [ + filter(k => splitKey(k)[0] === "customers") + ]); + + + const expectedRemainingKeys = allKeys.length - customerKeys.length; + + await collectionApi.delete("/customers"); + + const remainingKeys = keys(recordApi._storeHandle.data); + expect(remainingKeys.length).toBe(expectedRemainingKeys); + + }); + + it("should throw error when user user does not have permission", async () => { + const {collectionApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.removePermission(permission.manageCollection.get("/customers")); + expect(collectionApi.delete("/customers")).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {collectionApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withOnlyThisPermission(permission.manageCollection.get("/customers")); + await collectionApi.delete("/customers"); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/collectionApi.spec.js b/packages/core/test/collectionApi.spec.js new file mode 100644 index 0000000000..2a22a60e72 --- /dev/null +++ b/packages/core/test/collectionApi.spec.js @@ -0,0 +1,191 @@ +import {setupApphierarchy, basicAppHierarchyCreator_WithFields, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {includes, union} from "lodash"; +import {joinKey} from "../src/common"; + + +describe("collectionApi > getAllowedRecordTypes", () => { + + it("should list names of a collection's children", async () => { + const {collectionApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const allowedTypes = collectionApi.getAllowedRecordTypes("/customers"); + expect(allowedTypes).toEqual(["customer"]); + }); + +}); + + +describe("collectionApi > allids", () => { + + it("should add new record to comma separated, sharded allids file, then read back as id array", async () => { + const {collectionApi, recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer1 = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer1.surname = "thedog"; + + await recordApi.save(customer1); + + const customer2 = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer2.surname = "thedog"; + + await recordApi.save(customer2); + + const allIdsIterator = await collectionApi.getAllIdsIterator("/customers"); + let allIds = []; + + let shardIds = await allIdsIterator(); + while(shardIds.done === false) { + allIds = union(allIds, shardIds.result.ids); + shardIds = await allIdsIterator(); + } + + expect(allIds.length).toBe(2); + expect(includes(allIds, customer1.id)).toBeTruthy(); + expect(includes(allIds, customer2.id)).toBeTruthy(); + + }); + + it("delete record should remove id from allids shard", async () => { + const {collectionApi, recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer1 = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer1.surname = "thedog"; + + await recordApi.save(customer1); + + const customer2 = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer2.surname = "thedog"; + + await recordApi.save(customer2); + + await recordApi.delete(customer1.key); + + const allIdsIterator = await collectionApi.getAllIdsIterator("/customers"); + let allIds = []; + + let shardIds = await allIdsIterator(); + while(shardIds.done === false) { + allIds = union(allIds, shardIds.result.ids); + shardIds = await allIdsIterator(); + } + + expect(allIds.length).toBe(1); + expect(includes(allIds, customer2.id)).toBeTruthy(); + + }); + + it("should add and read record, that starts with any allowed key char (testing correct sharding of allids)", async () => { + + const allIdChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-".split(""); + + const {collectionApi, recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + for(let c of allIdChars) { + const customer = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer.surname = "thedog"; + const id = `${appHierarchy.customerRecord.nodeId}-${c}${customer.id.replace("0-","")}`; + customer.id = id; + await recordApi.save(customer); + } + + const allIdsIterator = await collectionApi.getAllIdsIterator("/customers"); + let allIds = []; + + let shardIds = await allIdsIterator(); + while(shardIds.done === false) { + allIds = union(allIds, shardIds.result.ids); + shardIds = await allIdsIterator(); + } + + expect(allIds.length).toBe(64); + }); + + it("should add nested record and read back", async () => { + const {collectionApi, recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey( + customer.key, "invoices" + ); + + const invoice = await recordApi.getNew( + invoiceCollectionKey, "invoice"); + + await recordApi.save(invoice); + + const allIdsIterator = await collectionApi.getAllIdsIterator( + appHierarchy.invoiceRecord.collectionNodeKey()); + + let allIds = []; + + let shardIds = await allIdsIterator(); + while(shardIds.done === false) { + allIds = union(allIds, shardIds.result.ids); + shardIds = await allIdsIterator(); + } + + expect(allIds.length).toBe(1); + expect(includes(allIds, invoice.id)).toBeTruthy(); + + }); + + it("should add double nested record, and read back", async () => { + const {collectionApi, recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = await recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), "customer"); + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey( + customer.key, "invoices" + ); + + const invoice = await recordApi.getNew( + invoiceCollectionKey, "invoice"); + + await recordApi.save(invoice); + + const chargeCollectionKey = joinKey( + invoice.key, "charges" + ); + + const charge = await recordApi.getNew( + chargeCollectionKey, "charge"); + + await recordApi.save(charge); + + const allIdsIterator = await collectionApi.getAllIdsIterator( + appHierarchy.chargeRecord.collectionNodeKey()); + + let allIds = []; + + let shardIds = await allIdsIterator(); + while(shardIds.done === false) { + allIds = union(allIds, shardIds.result.ids); + shardIds = await allIdsIterator(); + } + + expect(allIds.length).toBe(1); + expect(includes(allIds, charge.id)).toBeTruthy(); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/common.spec.js b/packages/core/test/common.spec.js new file mode 100644 index 0000000000..62257a77c8 --- /dev/null +++ b/packages/core/test/common.spec.js @@ -0,0 +1,257 @@ +import common, { isOneOf } from "../src/common"; +import _ from "lodash"; + +const lessThan = (than) => (compare) => compare < than + +describe("common > switchCase", () => { + + test("should return on first matching case", () => { + + const result = common.switchCase( + [lessThan(1), _.constant("first")], + [lessThan(2), _.constant("second")], + [lessThan(3), _.constant("third")] + )(1); + + expect(result).toBe("second"); + + }); + + test("should return undefined if case not matched", () => { + + const result = common.switchCase( + [lessThan(1), _.constant("first")], + [lessThan(2), _.constant("second")], + [lessThan(3), _.constant("third")] + )(10); + + expect(_.isUndefined(result)).toBeTruthy(); + + }); + +}); + +describe("common > allTrue", () => { + + test("should only return true when all conditions are met", () => { + + const result1 = common.allTrue( + lessThan(3), lessThan(5), lessThan(10) + )(1); + + expect(result1).toBeTruthy(); + + const result2 = common.allTrue( + lessThan(3), lessThan(5), lessThan(10) + )(7); + + expect(result2).toBeFalsy(); + + }); + +}); + +describe("common > anyTrue", () => { + + test("should return true when one or more condition is met", () => { + const result1 = common.anyTrue( + lessThan(3), lessThan(5), lessThan(10) + )(5); + + expect(result1).toBeTruthy(); + + const result2 = common.anyTrue( + lessThan(3), lessThan(5), lessThan(10) + )(4); + + expect(result2).toBeTruthy(); + }); + + test("should return false when no conditions are met", () => { + const result1 = common.anyTrue( + lessThan(3), lessThan(5), lessThan(10) + )(15); + + expect(result1).toBeFalsy(); + + }); + +}); + +const s = common.keySep; + +describe("common > getDirFromKey", () => { + test("should drop the final part of the path", () => { + const key = `${s}one${s}two${s}three${s}last`; + const expectedDIr = `${s}one${s}two${s}three`; + const result = common.getDirFomKey(key); + expect(result).toBe(expectedDIr); + }); + + test("should add leading /", () => { + const key = `one${s}two${s}three${s}last`; + const expectedDIr = `${s}one${s}two${s}three`; + const result = common.getDirFomKey(key); + expect(result).toBe(expectedDIr); + }); +}); + +describe("common > getFileFromKey", () => { + test("should get the final part of the path", () => { + const key = `one${s}two${s}three${s}last`; + const expectedFile = "last"; + const result = common.getFileFromKey(key); + expect(result).toBe(expectedFile); + }); +}); + +describe("common > getIndexKeyFromFileKey", () => { + test("should get the index key of the file's directory", () => { + const key = `one${s}two${s}three${s}file`; + const expectedFile = common.dirIndex(`one${s}two${s}three`); + const result = common.getIndexKeyFromFileKey(key); + expect(result).toBe(expectedFile); + }); +}); + +describe("common > somethingOrDefault", () => { + test("should use value if value is something", () => { + const result = common.somethingOrDefault( + "something", "default" + ); + expect(result).toBe("something"); + }); + test("should use value if value is empty sting", () => { + const result = common.somethingOrDefault( + "", "default" + ); + expect(result).toBe(""); + }); + test("should use value if value is empty array", () => { + const result = common.somethingOrDefault( + [], ["default"] + ); + expect(result.length).toBe(0); + }); + test("should use default if value is null", () => { + const result = common.somethingOrDefault( + null, "default" + ); + expect(result).toBe("default"); + }); + test("should use default if value is undefined", () => { + const result = common.somethingOrDefault( + {}.notDefined, "default" + ); + expect(result).toBe("default"); + }); +}); + +describe("common > dirIndex", () => { + it("should match /config/dir//dir.idx to path", () => { + var result = common.dirIndex("some/path"); + expect(result).toBe(`${s}.config${s}dir${s}some${s}path${s}dir.idx`); + }); +}); + + +describe("common > joinKey", () => { + it("should join an array with the key separator and leading separator", () => { + var result = common.joinKey("this", "is", "a", "path"); + expect(result).toBe(`${s}this${s}is${s}a${s}path`); + }); +}) + +describe("common > combinator ($$)", () => { + it("combines single params functions and returns a func", () => { + const f1 = str => str + " hello"; + const f2 = str => str + " there"; + const combined = common.$$(f1, f2); + const result = combined("mike says"); + expect(result).toBe("mike says hello there"); + }) +}) + +describe("common > pipe ($)", () => { + it("combines single params functions and executes with given param", () => { + const f1 = str => str + " hello"; + const f2 = str => str + " there"; + const result = common.$("mike says", [f1, f2]); + expect(result).toBe("mike says hello there"); + }) +}) + +describe("common > IsOneOf", () => { + + it("should return true when supplied value is in list of given vals", () => { + expect( + common.isOneOf("odo", "make")("odo") + ).toBe(true); + + expect( + common.isOneOf(1, 33, 9)(9) + ).toBe(true); + + expect( + common.isOneOf(true, false, "")(true) + ).toBe(true); + }); + + it("should return false when supplied value is not in list of given vals", () => { + expect( + common.isOneOf("odo", "make")("bob") + ).toBe(false); + + expect( + common.isOneOf(1, 33, 9)(999) + ).toBe(false); + + expect( + common.isOneOf(1, false, "")(true) + ).toBe(false); + }); + +}); + +describe("defineError", () => { + + it("should prefix and exception with message, and rethrow", () => { + + expect(() => + common.defineError(() => { + throw new Error("there") + }, "hello")) + .toThrowError("hello : there"); + }); + + it("should return function value when no exception", () => { + const result = common.defineError(() => 1, "no error"); + expect(result).toBe(1); + }); +}); + +describe("retry", () => { + + let counter = 0; + + it("should retry once", async () => { + var result = await common.retry(async () => 1, 3, 50); + expect(result).toBe(1); + }); + + it("should retry twice", async () => { + var result = await common.retry(async () => { + counter++; + if (counter < 2) throw 'error'; + return counter; + }, 3, 50); + expect(result).toBe(2); + }); + + it("throws error after 3 retries", async () => { + expect( + common.retry(async () => { counter++; throw counter; }, 3, 50) + ).rejects.toThrowError(4); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/getApis.spec.js b/packages/core/test/getApis.spec.js new file mode 100644 index 0000000000..abbb7032e6 --- /dev/null +++ b/packages/core/test/getApis.spec.js @@ -0,0 +1,86 @@ +import {getAppApis} from "../src"; +import {getMemoryTemplateApi, + createAppDefinitionWithActionsAndTriggers} from "./specHelpers"; +import {isFunction} from "lodash"; + +describe("getAppApis", () => { + + const getMemoryAppApis = async () => { + const {templateApi} = getMemoryTemplateApi(); + const rootNode = templateApi.getNewRootLevel(); + await templateApi.saveApplicationHierarchy(rootNode); + + return await getAppApis(templateApi._storeHandle, {}); + } + + it("should return api factory functions", async () => { + const apis = await getMemoryAppApis(); + expect(apis.recordApi).toBeDefined(); + expect(apis.templateApi).toBeDefined(); + expect(apis.collectionApi).toBeDefined(); + expect(apis.indexApi).toBeDefined(); + expect(apis.subscribe).toBeDefined(); + expect(apis.authApi).toBeDefined(); + }); + +}); + + +describe("getAppApis > initialiseActions", () => { + + it("should expose actions when all sources and behvaviours are present", async () => { + const {logs, emails, + call_timers, behaviourSources, + templateApi} = await createAppDefinitionWithActionsAndTriggers(); + const {actions} = await getAppApis( + templateApi._storeHandle, behaviourSources); + + actions.sendEmail("email"); + actions.measureCallTime("calltime"); + actions.logMessage("message"); + + expect(isFunction(actions.sendEmail)).toBeTruthy(); + expect(isFunction(actions.measureCallTime)).toBeTruthy(); + expect(isFunction(actions.logMessage)).toBeTruthy(); + + }); + + it("should throw exception when behaviour source is missing", async () => { + const {behaviourSources, templateApi} = await createAppDefinitionWithActionsAndTriggers(); + delete behaviourSources["my-custom-lib"]; + + let ex; + + try { + await getAppApis( + templateApi._storeHandle, behaviourSources); + } + catch (e) { + ex = e; + expect(e.message).toContain("behaviour"); + } + + expect(ex).toBeDefined(); + + }); + + it("should throw exception when behaviour is missing", async () => { + const {behaviourSources, templateApi} = await createAppDefinitionWithActionsAndTriggers(); + delete behaviourSources["my-custom-lib"]["send_email"]; + + let ex; + + try { + await getAppApis( + templateApi._storeHandle, behaviourSources); + } + catch (e) { + expect(e.message).toContain("behaviour"); + ex = e; + } + + expect(ex).toBeDefined(); + + }); + +}); \ No newline at end of file diff --git a/packages/core/test/indexApi.aggregates.spec.js b/packages/core/test/indexApi.aggregates.spec.js new file mode 100644 index 0000000000..4ea67048b1 --- /dev/null +++ b/packages/core/test/indexApi.aggregates.spec.js @@ -0,0 +1,149 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {permission} from "../src/authApi/permissions"; + +describe("aggregates", () => { + it("should calculate correct totals, when no condition supplied", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10); + await createInvoice(20); + await createInvoice(30); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.all_invoices_by_type.Important.count).toBe(3); + expect(result.all_invoices_by_type.Important.totalIncVat.max).toBe(30); + expect(result.all_invoices_by_type.Important.totalIncVat.min).toBe(10); + expect(result.all_invoices_by_type.Important.totalIncVat.sum).toBe(60); + expect(result.all_invoices_by_type.Important.totalIncVat.mean).toBe(60 / 3); + }); + + it("should split totals into correct groups", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10, 0, "Important"); + await createInvoice(20, 0, "NotImportant"); + await createInvoice(30, 0, "NotImportant"); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.all_invoices_by_type.Important.count).toBe(1); + expect(result.all_invoices_by_type.NotImportant.count).toBe(2); + expect(result.all_invoices_by_type.Important.totalIncVat.sum).toBe(10); + expect(result.all_invoices_by_type.NotImportant.totalIncVat.sum).toBe(50); + }); + + it("should include all when all match condition", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10, 0, "Important", true); + await createInvoice(10, 0, "Important", true); + await createInvoice(10, 0, "Important", true); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.written_off.Important.count).toBe(3); + expect(result.written_off.Important.totalIncVat.sum).toBe(30); + }); + + it("should add to '(none)' when group is blank, null or undefined", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10, 0, "", true); + await createInvoice(10, 0, null, true); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.all_invoices_by_type["(none)"].count).toBe(2); + expect(result.all_invoices_by_type["(none)"].totalIncVat.sum).toBe(20); + }); + + it("should not include those that do not match condition", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10, 0, "Important", true); + await createInvoice(10, 0, "Important", true); + await createInvoice(10, 0, "Important", false); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.written_off.Important.count).toBe(2); + expect(result.written_off.Important.totalIncVat.sum).toBe(20); + }); + + it("should have 'all' group when no grouping supplied", async () => { + const {createInvoice, indexApi} = await setup(); + await createInvoice(10, 0, "Important", true); + await createInvoice(20, 0, "Important", true); + const result = await indexApi.aggregates("/Outstanding Invoices"); + + expect(result.all_invoices.all.count).toBe(2); + }); + + it("should calculate correct totals, sharded index - all records", async () => { + const {createInvoice, indexApi, invoicesByOutstandingKey} = await setup(); + await createInvoice(10, 0, "Important"); + await createInvoice(20, 20, "Important"); + await createInvoice(30, 30, "Important"); + const result = await indexApi.aggregates(invoicesByOutstandingKey); + + expect(result.all_invoices_by_type.Important.count).toBe(3); + expect(result.all_invoices_by_type.Important.totalIncVat.max).toBe(30); + expect(result.all_invoices_by_type.Important.totalIncVat.min).toBe(10); + expect(result.all_invoices_by_type.Important.totalIncVat.sum).toBe(60); + expect(result.all_invoices_by_type.Important.totalIncVat.mean).toBe(60 / 3); + }); + + it("should calculate correct totals, sharded index - bounded by sharding", async () => { + const {createInvoice, indexApi, invoicesByOutstandingKey} = await setup(); + await createInvoice(10, 0, "Important"); + await createInvoice(20, 20, "Important"); + await createInvoice(30, 30, "Important"); + const result = await indexApi.aggregates( + invoicesByOutstandingKey, + {totalIncVat:10, paidAmount:10}, + {totalIncVat:10, paidAmount:10} + ); + + expect(result.all_invoices_by_type.Important.count).toBe(2); + expect(result.all_invoices_by_type.Important.totalIncVat.max).toBe(30); + expect(result.all_invoices_by_type.Important.totalIncVat.min).toBe(20); + expect(result.all_invoices_by_type.Important.totalIncVat.sum).toBe(50); + expect(result.all_invoices_by_type.Important.totalIncVat.mean).toBe(50 / 2); + }); + + it("should throw error when user user does not have permission", async () => { + const {app, indexApi} = await setup(); + app.removePermission(permission.readIndex.get("/customer_index")); + + let err; + try { + await indexApi.aggregates("/customer_index"); + } catch(e) { + err = e; + } + + expect(err).toBeDefined(); + expect(err.message.startsWith("Unauthorized")).toBeTruthy(); + //expect(indexApi.aggregates("/customer_index")).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {app, indexApi} = await setup(); + app.withOnlyThisPermission(permission.readIndex.get("/customer_index")); + await indexApi.aggregates("/customer_index"); + }); +}); + + +const setup = async () => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const customer = recordApi.getNew("/customers", "customer"); + await recordApi.save(customer); + + const createInvoice = async (totalAmount=10, paidAmount=0, type="Important", writtenOff=false) => { + const invoice = recordApi.getNew(`/customers/${customer.id}/invoices`, "invoice"); + invoice.totalIncVat = totalAmount; + invoice.paidAmount = paidAmount; + invoice.invoiceType = type; + invoice.isWrittenOff = writtenOff; + return await recordApi.save(invoice); + }; + + const invoicesByOutstandingKey = `/customers/${customer.id}/invoicesByOutstanding`; + + return {createInvoice, indexApi, invoicesByOutstandingKey, app}; +} \ No newline at end of file diff --git a/packages/core/test/indexApi.buildIndex.spec.js b/packages/core/test/indexApi.buildIndex.spec.js new file mode 100644 index 0000000000..621a6d1adb --- /dev/null +++ b/packages/core/test/indexApi.buildIndex.spec.js @@ -0,0 +1,639 @@ +import {setupApphierarchy, findCollectionDefaultIndex, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import { joinKey } from "../src/common"; +import {some} from "lodash"; +import {_deleteIndex} from "../src/indexApi/delete"; +import {permission} from "../src/authApi/permissions"; +import { getExactNodeForPath } from "../src/templateApi/hierarchy"; + +describe("buildIndex > Global index", () => { + + it("should index a record when record node is not decendant", async () => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const outstandingInvoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + outstandingInvoice.totalIncVat = 100; + outstandingInvoice.paidAmount = 50; + + await recordApi.save(outstandingInvoice); + + const paidInvoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + paidInvoice.totalIncVat = 200; + paidInvoice.paidAmount = 200; + + await recordApi.save(paidInvoice); + + const indexKey = appHierarchy.outstandingInvoicesIndex.nodeKey(); + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(indexItems[0].key).toBe(outstandingInvoice.key); + }); + + it("should index records from 2 seperate tree branches", async () => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + invoice.totalIncVat = 100; + invoice.paidAmount = 50; + + await recordApi.save(invoice); + + const partner = recordApi.getNew( + appHierarchy.partnerRecord.collectionNodeKey(), + "partner"); + + partner.surname = "thedog"; + + await recordApi.save(partner); + + const partnerInvoice = recordApi.getNewChild( + partner.key, + "invoices", + "invoice" + ); + + partnerInvoice.totalIncVat = 100; + partnerInvoice.paidAmount = 50; + + await recordApi.save(partnerInvoice); + + const indexKey = appHierarchy.outstandingInvoicesIndex.nodeKey(); + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(2); + expect(indexItems[0].key).toBe(invoice.key); + expect(indexItems[1].key).toBe(partnerInvoice.key); + + }); + +}); + +describe("buildIndex > TopLevelCollection", () => { + + it("should index a record when it is a nested decendant of the collection node", async() => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + invoice.totalIncVat = 100; + invoice.paidAmount = 50; + + await recordApi.save(invoice); + + const indexKey = appHierarchy.customerInvoicesIndex.nodeKey(); + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(indexItems[0].key).toBe(invoice.key); + + }); + + it("should not index a record when it is not decendant", async() => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const partner = recordApi.getNew( + appHierarchy.partnerRecord.collectionNodeKey(), + "partner"); + + partner.surname = "thedog"; + + await recordApi.save(partner); + + const invoice = recordApi.getNewChild( + partner.key, + "invoices", + "invoice" + ); + + invoice.totalIncVat = 100; + invoice.paidAmount = 50; + + await recordApi.save(invoice); + + const indexKey = appHierarchy.customerInvoicesIndex.nodeKey(); + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(0); + + }); + +}); + +describe("buildIndex > nested collection", () => { + + it("should build a single record into index", async () => { + + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + invoice.totalIncVat = 100; + invoice.paidAmount = 50; + + await recordApi.save(invoice); + + const indexKey = joinKey(customer.key, "invoice_index"); + await _deleteIndex(app, indexKey, false); + + const indexNode = getExactNodeForPath(appHierarchy.root)(indexKey); + await indexApi.buildIndex(indexNode.nodeKey()); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(indexItems[0].key).toBe(invoice.key); + + }); + + it("should build multiple records, from different parents into index", async () => { + + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const invoice = recordApi.getNewChild( + customer.key, + "invoices", + "invoice" + ); + + invoice.totalIncVat = 100; + invoice.paidAmount = 50; + + await recordApi.save(invoice); + + const customer2 = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer2.surname = "thedog"; + + await recordApi.save(customer2); + + const invoice2 = recordApi.getNewChild( + customer2.key, + "invoices", + "invoice" + ); + + invoice2.totalIncVat = 100; + invoice2.paidAmount = 50; + + await recordApi.save(invoice2); + + const indexKey = joinKey(customer.key, "invoice_index"); + await _deleteIndex(app, indexKey, false); + + const indexNode = getExactNodeForPath(appHierarchy.root)(indexKey); + await indexApi.buildIndex( + indexNode.nodeKey()); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(some(indexItems, i => i.key === invoice.key)).toBeTruthy(); + + const indexItems2 = await indexApi.listItems( + joinKey(customer2.key, "invoice_index") + ); + + expect(indexItems2.length).toBe(1); + expect(some(indexItems2, i => i.key === invoice2.key)).toBeTruthy(); + + }); + +}); + +describe("buildIndex > sharded index", () => { + + it("should index a record into a sharded index", async() => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + + customer.surname = "thedog"; + + await recordApi.save(customer); + + const indexKey = appHierarchy + .customersBySurnameIndex + .nodeKey(); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(indexItems[0].key).toBe(customer.key); + + }); + + it("should index multiple record into a sharded index", async() => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer1 = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer1.surname = "thedog"; + await recordApi.save(customer1); + + const customer2 = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer2.surname = "Zeecat"; + await recordApi.save(customer2); + + const indexKey = appHierarchy + .customersBySurnameIndex + .nodeKey(); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex(indexKey); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(2); + expect(some(indexItems, i => i.key === customer1.key)).toBeTruthy(); + expect(some(indexItems, i => i.key === customer2.key)).toBeTruthy(); + + }); + + it("should index multiple records into a sharded and nested index", async() => { + const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer.surname = "thedog"; + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey( + customer.key, "invoices"); + + const invoice1 = recordApi.getNew( + invoiceCollectionKey, + "invoice" + ); + invoice1.totalIncVat = 10; + invoice1.paidAmount = 10; + await recordApi.save(invoice1); + + const invoice2 = recordApi.getNew( + invoiceCollectionKey, + "invoice" + ); + invoice2.totalIncVat = 10; + invoice2.paidAmount = 0; + await recordApi.save(invoice2); + + const indexKey = joinKey( + customer.key, + appHierarchy.invoicesByOutstandingIndex.name); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex( + appHierarchy.invoicesByOutstandingIndex.nodeKey()); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(2); + expect(some(indexItems, i => i.key === invoice1.key)).toBeTruthy(); + expect(some(indexItems, i => i.key === invoice2.key)).toBeTruthy(); + + const outstandingRange = {totalIncVat:1, paidAmount:0}; + const outstandingItems = await indexApi.listItems(indexKey, { + rangeStartParams: outstandingRange, + rangeEndParams: outstandingRange + }); + expect(outstandingItems.length).toBe(1); + + }); + + it("should build reverse reference index", async () => { + const {recordApi, indexApi, appHierarchy, app} = + await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const referencedCustomer = recordApi.getNew("/customers", "customer"); + referencedCustomer.surname = "Zecat"; + await recordApi.save(referencedCustomer); + + const referencingCustomer = recordApi.getNew("/customers", "customer"); + referencingCustomer.surname = "Ledog"; + referencingCustomer.referredBy = { + key: referencedCustomer.key, value: referencedCustomer.surname + }; + referencingCustomer.isalive = true; + + await recordApi.save(referencingCustomer); + + const indexKey = joinKey(referencedCustomer.key, "referredToCustomers"); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex( + appHierarchy.referredToCustomersReverseIndex.nodeKey()); + + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(some(indexItems, i => i.key === referencingCustomer.key)).toBeTruthy(); + }); + +}); + +describe("buildIndex > reverse reference index", () => { + + it("should build a single record into index", async () => { + const {recordApi, indexApi, appHierarchy, app} = + await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + customer.isalive = true; + + await recordApi.save(customer); + + const indexKey = joinKey(partner1.key, "partnerCustomers"); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex( + appHierarchy.partnerCustomersReverseIndex.nodeKey()); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(some(indexItems, i => i.key === customer.key)).toBeTruthy(); + }); + + it("should build multiple records into an index, when referencing same record", async () => { + const {recordApi, indexApi, appHierarchy, app} = + await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const customer1 = recordApi.getNew("/customers", "customer"); + customer1.surname = "Ledog"; + customer1.partner = { + key: partner1.key, value: partner1.businessName + }; + customer1.isalive = true; + + await recordApi.save(customer1); + + const customer2 = recordApi.getNew("/customers", "customer"); + customer2.surname = "Zeecat"; + customer2.partner = { + key: partner1.key, value: partner1.businessName + }; + customer2.isalive = true; + + await recordApi.save(customer2); + + const indexKey = joinKey(partner1.key, "partnerCustomers"); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex( + appHierarchy.partnerCustomersReverseIndex.nodeKey()); + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(2); + expect(some(indexItems, i => i.key === customer1.key)).toBeTruthy(); + expect(some(indexItems, i => i.key === customer2.key)).toBeTruthy(); + }); + + + it("should build multiple records into seperate indexes, when referencing different records", async () => { + const {recordApi, indexApi, appHierarchy, app} = + await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const partner2 = recordApi.getNew("/partners", "partner"); + partner2.businessName = "ACME inc"; + await recordApi.save(partner2); + + const customer1 = recordApi.getNew("/customers", "customer"); + customer1.surname = "Ledog"; + customer1.partner = { + key: partner1.key, value: partner1.businessName + }; + customer1.isalive = true; + + await recordApi.save(customer1); + + const customer2 = recordApi.getNew("/customers", "customer"); + customer2.surname = "Zeecat"; + customer2.partner = { + key: partner2.key, value: partner1.businessName + }; + customer2.isalive = true; + + await recordApi.save(customer2); + + const indexKey1 = joinKey(partner1.key, "partnerCustomers"); + const indexKey2 = joinKey(partner2.key, "partnerCustomers"); + + await _deleteIndex(app, indexKey1, false); + + await _deleteIndex(app, indexKey2, false); + + await indexApi.buildIndex( + appHierarchy.partnerCustomersReverseIndex.nodeKey()); + + const indexItems1 = await indexApi.listItems(indexKey1); + + expect(indexItems1.length).toBe(1); + expect(some(indexItems1, i => i.key === customer1.key)).toBeTruthy(); + + const indexItems2 = await indexApi.listItems(indexKey2); + + expect(indexItems2.length).toBe(1); + expect(some(indexItems2, i => i.key === customer2.key)).toBeTruthy(); + }); + + it.skip("should build record into index, when referencing and referenced records are in multiple nested collections", async () => { + + // this currently fails because it is currntly assumed that the reference index should be either + // - Top level index + // - An ancestor index + // this test sets "customers//invoices//charges/""to point to.. + // "/partners//invoices/default" + // + // To work as intended, we would need to somehow find the index by: + // - customer.partner.key + /invoices/default + // bearing in mind that the customer is an ancestor. + + const {recordApi, indexApi, appHierarchy, app} = + await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes + ); + + const partner = recordApi.getNew("/partners", "partner"); + partner.businessName = "ACME inc"; + await recordApi.save(partner); + + const partnerInvoice = recordApi.getNew( + joinKey(partner.key, "invoices"), "invoice" + ); + await recordApi.save(partnerInvoice); + + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + await recordApi.save(customer); + + const customerInvoice = recordApi.getNew( + joinKey(customer.key, "invoices"), "invoice" + ); + await recordApi.save(customerInvoice); + + const charge = recordApi.getNew( + joinKey(customerInvoice.key, "charges"), "charge" + ); + charge.partnerInvoice = { + key: partnerInvoice.key, createdDate: "something" + }; + await recordApi.save(charge); + + + const indexKey = joinKey(partnerInvoice.key, "partnerCharges"); + + await _deleteIndex(app, indexKey, false); + + await indexApi.buildIndex( + appHierarchy.partnerChargesReverseIndex.nodeKey()); + + const indexItems = await indexApi.listItems(indexKey); + + expect(indexItems.length).toBe(1); + expect(some(indexItems, i => i.key === charge.key)).toBeTruthy(); + }); + + it("should throw error when user user does not have permission", async () => { + const {indexApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + app.removePermission(permission.manageIndex.get()); + expect( + indexApi.buildIndex(appHierarchy.partnerCustomersReverseIndex.nodeKey()) + ).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {app, indexApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + app.withOnlyThisPermission(permission.manageIndex.get()); + await indexApi.buildIndex(appHierarchy.partnerCustomersReverseIndex.nodeKey()); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/indexApi.list.spec.js b/packages/core/test/indexApi.list.spec.js new file mode 100644 index 0000000000..e61cf97173 --- /dev/null +++ b/packages/core/test/indexApi.list.spec.js @@ -0,0 +1,155 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {permission} from "../src/authApi/permissions"; + +describe("indexApi > listItems", () => { + + it("should pull items from one shard when only startRange and endRange params are equal", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const items_L_shard = await indexApi.listItems( + "/customersBySurname", { + rangeStartParams: {surname:"L"}, + rangeEndParams: {surname:"L"} + }); + expect(items_L_shard.length).toBe(1); + expect(items_L_shard[0].key).toBe(record1.key); + + const items_Z_shard = await indexApi.listItems( + "/customersBySurname", { + rangeStartParams: {surname:"Z"}, + rangeEndParams: {surname:"Z"} + }); + expect(items_Z_shard.length).toBe(1); + expect(items_Z_shard[0].key).toBe(record2.key); + + }); + + it("should pull items from one shard when shard is within startRange and endRange params", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const items_L_shard = await indexApi.listItems( + "/customersBySurname", { + rangeStartParams: {surname:"K"}, + rangeEndParams: {surname:"M"} + }); + expect(items_L_shard.length).toBe(1); + expect(items_L_shard[0].key).toBe(record1.key); + + }); + + it("should pull items from multiple shards are withing startRange and endRange params", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const items_L_shard = await indexApi.listItems( + "/customersBySurname", { + rangeStartParams: {surname:"K"}, + rangeEndParams: {surname:"Z"} + }); + expect(items_L_shard.length).toBe(2); + expect(items_L_shard[0].key).toBe(record1.key); + + }); + + it("should filter items by given search phrase", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const results = await indexApi.listItems("/customer_index",{searchPhrase:"*cat"}); + expect(results.length).toBe(1); + expect(results[0].surname).toBe("Zeecat"); + }); + + it("should filter items by given search phrase, accross sharded whole index", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const results = await indexApi.listItems("/customersBySurname",{searchPhrase:"*cat"}); + expect(results.length).toBe(1); + expect(results[0].surname).toBe("Zeecat"); + }); + + it("should filter items by given search phrase, in single shard ", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Lecat"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const record3 = recordApi.getNew("/customers", "customer"); + record3.surname = "Zeedog"; + await recordApi.save(record3); + + const results = await indexApi.listItems("/customersBySurname",{ + searchPhrase:"*cat", + rangeStartParams: {surname:"Z"}, + rangeEndParams: {surname:"Z"} + }); + expect(results.length).toBe(1); + expect(results[0].surname).toBe("Zeecat"); + }); + + it("should throw error when user user does not have permission", async () => { + const {indexApi, app} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + app.removePermission(permission.readIndex.get("/customersBySurname")); + expect(indexApi.listItems("/customersBySurname")).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {app, indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + app.withOnlyThisPermission(permission.readIndex.get("/customersBySurname")); + await indexApi.listItems("/customersBySurname"); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/indexing.concurrency.spec.js b/packages/core/test/indexing.concurrency.spec.js new file mode 100644 index 0000000000..f3d73bdf2c --- /dev/null +++ b/packages/core/test/indexing.concurrency.spec.js @@ -0,0 +1,269 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {joinKey} from "../src/common"; +import {getLockFileContent} from "../src/common/lock"; +import {some, isArray} from "lodash"; +import {cleanup} from "../src/transactions/cleanup"; +import {LOCK_FILE_KEY} from "../src/transactions/transactionsCommon"; + + +describe("cleanup transactions", () => { + + it("testing disable of cleanupTransactions, just for test purposes", async () => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Zeecat"; + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Ledog"; + + await recordApi.save(record1); + await recordApi.save(record2); + + const records = await indexApi.listItems("/customer_index"); + + // cleanup should be disabled as above + expect(records.length).toBe(0); + }); + + it("should index 2 new create transactions", async () => { + // cleanup is disabled, with true parameter + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Zeecat"; + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Ledog"; + + await recordApi.save(record1); + await recordApi.save(record2); + + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(2); + expect(some(records, r => r.surname === "Zeecat")).toBeTruthy(); + expect(some(records, r => r.surname === "Ledog")).toBeTruthy(); + }); + + it("when create and update transaction for the same record, index should be latest record", async () => { + + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + expect(some(records, r => r.surname === "Zeecat")).toBeTruthy(); + + }); + + it("should choose current version of record when multiple update transactions found", async () => { + + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + + savedRecord.surname = "Afish"; + await recordApi.save(savedRecord); + + savedRecord.surname = "Lelapin"; + await recordApi.save(savedRecord); + + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + expect(some(records, r => r.surname === "Lelapin")).toBeTruthy(); + + }); + + it("should not reindex when transactionId does not match that of the record", async () => { + + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + + await cleanup(app); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + + savedRecord.transactionId = "something else"; + await recordApi._storeHandle.updateJson( + joinKey(savedRecord.key, "record.json"), + savedRecord); + + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + expect(records[0].surname).toBe("Ledog"); + + }); + + it("should not reindex when transactionId does not match that of the record, and has multiple transactions", async () => { + + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + + await cleanup(app); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + + savedRecord.surname = "Lefish"; + await recordApi.save(savedRecord); + + savedRecord.transactionId = "something else"; + await recordApi._storeHandle.updateJson( + joinKey(savedRecord.key, "record.json"), + savedRecord); + + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + expect(records[0].surname).toBe("Ledog"); + + }); + + it("should remove from index when delete and update transactions exists", async () => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + + await cleanup(app); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + await recordApi.delete(savedRecord.key); + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(0); + }); + + it("should not add to index when create and delete found", async () => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + await recordApi.delete(savedRecord.key); + await cleanup(app); + + const records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(0); + }); + + it("should correctly remove from indexes, when multiple update transactions exist", async() => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.isalive = false; + const savedRecord = await recordApi.save(record); + await cleanup(app); + + const preUpdateRecords = await indexApi.listItems("/deceased"); + expect(preUpdateRecords.length).toBe(1); + + savedRecord.surname = "Ledog"; + await recordApi.save(savedRecord); + savedRecord.isalive = true; + await recordApi.save(savedRecord); + + await cleanup(app); + const records = await indexApi.listItems("/deceased"); + expect(records.length).toBe(0); + }); + + it("should not add to index when created, then updated to be filtered out of index", async() => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.isalive = false; + const savedRecord = await recordApi.save(record); + savedRecord.surname = "Ledog"; + savedRecord.isalive = true; + await recordApi.save(savedRecord); + + await cleanup(app); + + const records = await indexApi.listItems("/deceased"); + expect(records.length).toBe(0); + }); + + it("should do nothing when lockfile exists", async() => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + const savedRecord = await recordApi.save(record); + const currentTime = await app.getEpochTime(); + await recordApi._storeHandle.createFile( + LOCK_FILE_KEY, + getLockFileContent(30000, (currentTime + 30000)) + ); + + await cleanup(app); + + let records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(0); + + await recordApi._storeHandle.deleteFile(LOCK_FILE_KEY); + await cleanup(app); + records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + + }); + + it("should take control when lockfile is timedout", async() => { + const {recordApi, app, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes, true); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + const savedRecord = await recordApi.save(record); + await recordApi._storeHandle.createFile( + LOCK_FILE_KEY, + getLockFileContent(30000, (new Date(1990,1,1,0,0,0,0).getTime())) + ); + + await cleanup(app); + + let records = await indexApi.listItems("/customer_index"); + expect(records.length).toBe(1); + + }); + + +}) \ No newline at end of file diff --git a/packages/core/test/indexing.createIndexFile.spec.js b/packages/core/test/indexing.createIndexFile.spec.js new file mode 100644 index 0000000000..ca9a1d7f45 --- /dev/null +++ b/packages/core/test/indexing.createIndexFile.spec.js @@ -0,0 +1,21 @@ +import {getMemoryStore} from "./specHelpers"; +import {createIndexFile} from "../src/indexing/sharding"; +import {uniqueIndexName} from "../src/indexing/read"; +import {includes} from "lodash"; + +describe("indexing.createIndexFile", () => { + + it("should create an empty document .csv", async () => { + const datastore = getMemoryStore(); + + const index = {map : "return {name: record.name, age: record.age}", filter:""}; + const indexKey = `/customers/hello`; + await datastore.createFolder("/customers"); + await createIndexFile(datastore, indexKey, index); + + const savedIndex = await datastore.loadFile(indexKey); + + expect(savedIndex).toBe(""); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/indexing.evaluate.spec.js b/packages/core/test/indexing.evaluate.spec.js new file mode 100644 index 0000000000..53ff2b0716 --- /dev/null +++ b/packages/core/test/indexing.evaluate.spec.js @@ -0,0 +1,138 @@ +import evaluate from "../src/indexing/evaluate"; +import {constant, merge} from "lodash"; + +const getRecord = obj => { + const def = { + key : "abcd1234", + type : "test", + isNew : false, + id : "1234" + } + + const newObj = merge(def, obj); + return newObj; +}; + +describe("index evaluation", () => { + + it("should filter out when object does not pass filter", () => { + + const index = { + filter : "record.type === 'customer'", + fields: { + type : {type:"string"} + } + }; + + const record = getRecord({ + type : "not a customer" + }); + + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(false); + + }); + + it("should always include key with the record", () => { + + const index = { + filter : "record.type === 'customer'", + fields: { + type : {type:"string"} + } + }; + + const record = getRecord({ + type : "customer" + }); + + const key = record.key; + + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(true); + expect(result.result.key).toBe(key); + + }); + + it("should map when filter test is passed", () => { + const index = { + filter : "record.type === 'customer'", + map: "return {newName: record.name + 'by', email: record.email }", + fields: { + newName : {type:"string"}, + email: {type:"string"} + } + }; + const record = getRecord({ + type : "customer", + name: "bob", + email: "bob@budibase.com" + }); + + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(true); + expect(result.result.newName).toBeDefined(); + expect(result.result.newName).toBe("bobby"); + expect(result.result.email).toBe("bob@budibase.com"); + + }); + + it("should not need a filter", () => { + const index = { + map : "return {newName : record.name + ' Thedog'}", + fields: { + newName : {type:"string"} + } + }; + const record = getRecord({ + name : "bob" + }); + + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(true); + expect(result.result.newName).toBeDefined(); + expect(result.result.newName).toBe("bob Thedog"); + }); + + it("should return all declared fields when no map supplied", () => { + const index = { + filter : "record.type === 'customer'", + fields : { + type: {type:"string"}, + name: {type:"string"} + } + }; + const record = getRecord({ + type : "customer", + name: "bob" + }); + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(true); + expect(result.result.name).toBeDefined(); + expect(result.result.name).toBe("bob"); + }); + + it("should set undefined mapped members to null", () => { + const index = { + map : "return {firstname : record.firstname, surname : record.surname}", + fields: { + newName : {type:"string"} + } + }; + const record = getRecord({}); + + const result = evaluate(record)(index); + expect(result.isError).toBe(false); + expect(result.passedFilter).toBe(true); + expect(result.result.firstname).toBeDefined(); + expect(result.result.surname).toBeDefined(); + expect(result.result.firstname).toBeNull(); + expect(result.result.surname).toBeNull(); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/indexing.getRelevantIndexes.spec.js b/packages/core/test/indexing.getRelevantIndexes.spec.js new file mode 100644 index 0000000000..5d6714bdb0 --- /dev/null +++ b/packages/core/test/indexing.getRelevantIndexes.spec.js @@ -0,0 +1,143 @@ +import {getMemoryTemplateApi, + basicAppHierarchyCreator_WithFields, + setupApphierarchy, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {getRelevantReverseReferenceIndexes, + getRelevantAncestorIndexes} from "../src/indexing/relevant"; +import {some} from "lodash"; +import {joinKey} from "../src/common"; + +describe("getRelevantIndexes", () => { + + it("should get indexes only, when key is root level record", async () => { + const {appHierarchy} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes); + + const heirarchalIndexesByPath = getRelevantAncestorIndexes( + appHierarchy.root, { + appName:"hello", + key: "/settings" + } + ); + + const reverseReferenceIndexesByPath = getRelevantReverseReferenceIndexes( + appHierarchy.root, { + appName:"hello", + key: "/settings" + } + ); + + expect(heirarchalIndexesByPath.length).toBe(0); + expect(reverseReferenceIndexesByPath.length).toBe(0); + + }); + + it("should get collection default index, when key is child of root level collection", async () => { + const {recordApi, + appHierarchy} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes); + + const customer = recordApi.getNew("/customers", "customer"); + + const indexes = getRelevantAncestorIndexes( + appHierarchy.root, customer); + + expect(indexes.length).toBe(4); + + const indexExists = key => + some(indexes, c => c.indexKey === key); + + expect(indexExists("/customer_index")).toBeTruthy(); + expect(indexExists("/deceased")).toBeTruthy(); + expect(indexExists("/customersBySurname")).toBeTruthy(); + }); + + it("should ignore index when allowedRecordNodeIds does not contain record's node id", async () => { + const {recordApi, + appHierarchy} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes); + + const customer = recordApi.getNew("/customers", "customer"); + const invoice = recordApi.getNew(joinKey(customer.key, "invoices"), "invoice"); + + const indexes = getRelevantAncestorIndexes( + appHierarchy.root, invoice); + + const indexExists = key => + some(indexes, c => c.indexKey === key); + + expect(indexExists("/customersBySurname")).toBeFalsy(); + }); + + it("should include index when allowedRecordNodeIds contains record's node id", async () => { + const {recordApi, + appHierarchy} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes); + + const customer = recordApi.getNew("/customers", "customer"); + + const indexes = getRelevantAncestorIndexes( + appHierarchy.root, customer); + + expect(indexes.length).toBe(4); + + const indexExists = key => + some(indexes, c => c.indexKey === key); + + expect(indexExists("/customersBySurname")).toBeTruthy(); + }); + + it("should get default index and relevant parent index when record is 2 nested collections deep", async () => { + const {recordApi, appHierarchy} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields_AndIndexes); + + const nodeid = appHierarchy.customerRecord.nodeId; + const invoice = recordApi.getNew(`/customers/${nodeid}-1234/invoices`, "invoice") + + const indexes = getRelevantAncestorIndexes( + appHierarchy.root, invoice); + + expect(indexes.length).toBe(4); + expect(some(indexes, i => i.indexKey === `/customer_invoices`)).toBeTruthy(); + expect(some(indexes, i => i.indexKey === `/customers/${nodeid}-1234/invoice_index`)).toBeTruthy(); + }); + + it("should get reverseReferenceIndex accross hierarchy branches", async () => { + const {appHierarchy, + recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const partner = recordApi.getNew("/partners", "partner"); + partner.businessName = "acme inc"; + //await recordApi.save(partner); + + const customer = recordApi.getNew("/customers", "customer"); + customer.partner = {key:partner.key, value:partner.businessName}; + //await recordApi.save(customer); + + + const indexes = getRelevantReverseReferenceIndexes( + appHierarchy.root, customer); + expect(indexes.length).toBe(1); + expect(indexes[0].indexKey) + .toBe(joinKey(partner.key, appHierarchy.partnerCustomersReverseIndex.name)); + + + }); + + it("should get reverseReferenceIndex when referencing record in same collection", async () => { + const {appHierarchy, + recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const referredByCustomer = recordApi.getNew("/customers", "customer"); + referredByCustomer.surname = "ledog"; + + const referredToCustomer = recordApi.getNew("/customers", "customer"); + referredToCustomer.referredBy = {key:referredByCustomer.key, value:"ledog"}; + + const indexes = getRelevantReverseReferenceIndexes( + appHierarchy.root, referredToCustomer); + expect(indexes.length).toBe(1); + expect(indexes[0].indexKey) + .toBe(joinKey(referredByCustomer.key, appHierarchy.referredToCustomersReverseIndex.name)); + }); +}); \ No newline at end of file diff --git a/packages/core/test/indexing.schema.spec.js b/packages/core/test/indexing.schema.spec.js new file mode 100644 index 0000000000..25bc0ee757 --- /dev/null +++ b/packages/core/test/indexing.schema.spec.js @@ -0,0 +1,132 @@ +import {generateSchema} from "../src/indexing/indexSchemaCreator"; +import {setupApphierarchy, findCollectionDefaultIndex} from "./specHelpers"; +import {find} from "lodash"; +import {indexTypes} from "../src/templateApi/indexes"; + +describe("indexSchemGenerator", () => { + + it("should return mapped columns of single type, when accepts all in collection of one type", async () => { + const {appHierarchy} = await setup(false); + const schema = generateSchema(appHierarchy.root, appHierarchy.petsIndex); + schemaHasFieldOfType(schema, "key", "string"); + schemaHasFieldOfType(schema, "sortKey", "string"); + schemaHasFieldOfType(schema, "id", "string"); + schemaHasFieldOfType(schema, "type", "string"); + schemaHasFieldOfType(schema, "isNew", "bool"); + schemaHasFieldOfType(schema, "name", "string"); + schemaHasFieldOfType(schema, "dob", "datetime"); + schemaHasFieldOfType(schema, "isAlive", "bool"); + expect(schema.length).toBe(8); + }); + + it("should return mapped columns of two types, when accepts all in collection or two typs", async () => { + const {appHierarchy} = await setup(true); + const schema = generateSchema(appHierarchy.root, appHierarchy.petsIndex); + schemaHasFieldOfType(schema, "key", "string"); + schemaHasFieldOfType(schema, "sortKey", "string"); + schemaHasFieldOfType(schema, "id", "string"); + schemaHasFieldOfType(schema, "type", "string"); + schemaHasFieldOfType(schema, "isNew", "bool"); + schemaHasFieldOfType(schema, "name", "string"); + schemaHasFieldOfType(schema, "dob", "datetime"); + schemaHasFieldOfType(schema, "isAlive", "bool"); + schemaHasFieldOfType(schema, "noOfGills", "number"); + schemaHasFieldOfType(schema, "favouriteFish", "reference"); + expect(schema.length).toBe(10); + }); + + it("should return mapped columns of one types, when accepts only onw of two types", async () => { + const {appHierarchy} = await setup(true); + const schema = generateSchema(appHierarchy.root, appHierarchy.fishOnlyIndex); + schemaHasFieldOfType(schema, "key", "string"); + schemaHasFieldOfType(schema, "sortKey", "string"); + schemaHasFieldOfType(schema, "id", "string"); + schemaHasFieldOfType(schema, "type", "string"); + schemaHasFieldOfType(schema, "isNew", "bool"); + schemaHasFieldOfType(schema, "name", "string"); + schemaHasFieldOfType(schema, "isAlive", "bool"); + schemaHasFieldOfType(schema, "noOfGills", "number"); + expect(schema.length).toBe(8); + }); + + it("should return mapped columns type, for reverse reference index", async () => { + const {appHierarchy} = await setup(true); + const schema = generateSchema(appHierarchy.root, appHierarchy.dogFriends); + schemaHasFieldOfType(schema, "key", "string"); + schemaHasFieldOfType(schema, "sortKey", "string"); + schemaHasFieldOfType(schema, "id", "string"); + schemaHasFieldOfType(schema, "type", "string"); + schemaHasFieldOfType(schema, "isNew", "bool"); + schemaHasFieldOfType(schema, "name", "string"); + schemaHasFieldOfType(schema, "isAlive", "bool"); + schemaHasFieldOfType(schema, "dob", "datetime"); + schemaHasFieldOfType(schema, "favouriteFish", "reference"); + expect(schema.length).toBe(9); + }); + +}); + +const schemaHasFieldOfType = (schema, fieldname, type) => { + const field = find(schema, f => f.name === fieldname); + const fname = !field ? "field not found" : field.name; + expect(fname).toBe(fieldname); + expect(field.type).toBe(type); +} + +const setup = includeFish => + setupApphierarchy(createApp(includeFish)); + +const createApp = (includeFish) => (templateApi) => { + + const root = templateApi.getNewRootLevel(); + + const dogRecord = templateApi.getNewRecordTemplate(root, "dog"); + + const addField = (recordNode) => (name, type, typeOptions) => { + const field = templateApi.getNewField(type); + field.name = name; + if(typeOptions) field.typeOptions = typeOptions; + templateApi.addField(recordNode, field); + return field; + }; + + const petsIndex = templateApi.getNewIndexTemplate(root); + petsIndex.name = "allPets"; + petsIndex.allowedRecordNodeIds = [dogRecord.nodeId]; + + const addDogField = addField(dogRecord); + addDogField("name", "string"); + addDogField("dob", "datetime"); + addDogField("isAlive", "bool"); + + let fishStuff = {}; + if(includeFish) { + const fishRecord = templateApi.getNewRecordTemplate(root, "fish"); + const addFishField = addField(fishRecord); + addFishField("name", "string"); + addFishField("isAlive", "bool"); + addFishField("noOfGills", "number"); + fishStuff.fishRecord = fishRecord; + const fishOnlyIndex = templateApi.getNewIndexTemplate(root); + fishOnlyIndex.name = "fishOnly"; + fishOnlyIndex.allowedRecordNodeIds = [fishRecord.nodeId]; + fishStuff.fishOnlyIndex = fishOnlyIndex; + + const dogFriends = templateApi.getNewIndexTemplate(dogRecord, indexTypes.reference); + dogFriends.name = "dogFriends"; + fishStuff.dogFriends = dogFriends; + + petsIndex.allowedRecordNodeIds.push(fishRecord.nodeId); + + const favFishField = addDogField("favouriteFish", "reference", { + indexNodeKey : fishOnlyIndex.nodeKey(), + reverseIndexNodeKeys : [dogFriends.nodeKey()], + displayValue : "name" + }); + fishStuff.favFishField = favFishField; + } + + return ({ + dogRecord, petsIndex, root, ...fishStuff + }) +}; diff --git a/packages/core/test/initialiseData.spec.js b/packages/core/test/initialiseData.spec.js new file mode 100644 index 0000000000..5ab440960e --- /dev/null +++ b/packages/core/test/initialiseData.spec.js @@ -0,0 +1,74 @@ +import {getMemoryTemplateApi, basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {initialiseData} from "../src/appInitialise/initialiseData"; +import {TRANSACTIONS_FOLDER} from "../src/transactions/transactionsCommon"; +import {AUTH_FOLDER, USERS_LIST_FILE, ACCESS_LEVELS_FILE} from "../src/authApi/authCommon"; + +describe("initialiseData", () => { + + it("should create csv file for each index, when does not exist", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + + expect(await datastore.exists(`/customer_index/index.csv`)).toBeTruthy(); + expect(await datastore.exists(`/customer_index`)).toBeTruthy(); + expect(await datastore.exists(`/deceased/index.csv`)).toBeTruthy(); + expect(await datastore.exists(`/deceased`)).toBeTruthy(); + }); + + it("should create folder for collection", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + expect(await datastore.exists(`/customers`)).toBeTruthy(); + }); + + + it("should create allids folders", async () => { + const {appDef, datastore, h} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + + const allIdsTypeFolder = "/customers/allids/" + h.customerRecord.nodeId; + const allIdsFolder = "/customers/allids"; + expect(await datastore.exists(allIdsTypeFolder)).toBeTruthy(); + expect(await datastore.exists(allIdsFolder)).toBeTruthy(); + }); + + it("should create transactions folder", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + expect(await datastore.exists(TRANSACTIONS_FOLDER)).toBeTruthy(); + }); + + it("should create auth folder", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + expect(await datastore.exists(AUTH_FOLDER)).toBeTruthy(); + }); + + it("should create users list", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + expect(await datastore.exists(USERS_LIST_FILE)).toBeTruthy(); + }); + + it("should create access levels file", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef); + expect(await datastore.exists(ACCESS_LEVELS_FILE)).toBeTruthy(); + }); + + it("should create access levels file, with supplied object", async () => { + const {appDef, datastore} = getApplicationDefinition(); + await initialiseData(datastore, appDef, { version: 0, levels: [{ + name:"owner", permissions:[{type:"create user"}] + }] }); + const levels = await datastore.loadJson(ACCESS_LEVELS_FILE); + expect(levels.levels[0].name).toBe("owner"); + }); + + const getApplicationDefinition = () => { + const {templateApi, app} = getMemoryTemplateApi(); + const h = basicAppHierarchyCreator_WithFields_AndIndexes(templateApi); + return {appDef:{hierarchy:h.root, actions:[], triggers:[]}, datastore:app.datastore, h}; + } + +}); \ No newline at end of file diff --git a/packages/core/test/memory.js b/packages/core/test/memory.js new file mode 100644 index 0000000000..11da8a5fd3 --- /dev/null +++ b/packages/core/test/memory.js @@ -0,0 +1,135 @@ +import {isUndefined, has} from "lodash"; +import {take} from "lodash/fp"; +import {Readable, Writable} from "readable-stream"; +import { Buffer } from "safe-buffer"; +import {splitKey, joinKey, $} from "../src/common"; +import {getLastPartInKey} from "../src/templateApi/hierarchy"; + +const folderMarker = "OH-YES-ITSA-FOLDER-"; +const isFolder = val => val.includes(folderMarker); + +const getParentFolderKey = key => + $(key, [ + splitKey, + take((splitKey(key).length - 1)), + joinKey, + ]); + +const getParentFolder = (data,key) => { + const parentKey = getParentFolderKey(key); + if(data[parentKey] === undefined) + throw new Error("Parent folder for " + key + " does not exist (" + parentKey + ")"); + return JSON.parse(data[parentKey]); +} + +const addItemToParentFolder = (data, path) => { + if(getParentFolderKey(path) === "/") return; + const parentFolder = getParentFolder(data, path); + parentFolder.items.push( + getLastPartInKey(path)); + data[getParentFolderKey(path)] = JSON.stringify(parentFolder); +} + +export const createFile = data => async (path, content) => { + if(await exists(data)(path)) { + throw new Error(path + " already exists"); + } + addItemToParentFolder(data, path); + data[path] = content; +}; +export const updateFile = data => async (path, content) => { + // putting this check in to force use of create + if(!await exists(data)(path)) throw new Error("cannot update " + path + " - does not exist"); + data[path] = content; +} + +export const writableFileStream = data => async (path) => { + //if(!await exists(data)(path)) throw new Error("cannot write stream to " + path + " - does not exist"); + const stream = Writable(); + stream._write = (chunk, encoding, done) => { + data[path] = data[path] === undefined + ? [] : data[path]; + data[path] = [...data[path], ...chunk]; + done(); + }; + return stream; +}; + +export const readableFileStream = data => async (path) => { + if(!await exists(data)(path)) + throw new Error("cannot read stream from " + path + " - does not exist"); + const s = new Readable(); + s._read = () => { + s.push(Buffer.from(data[path])); + s.push(null); + }; + return s; +}; + +export const getFileSize = data => async (path) => { + if(!await exists(data)(path)) + throw new Error("cannot get size of " + path + " - does not exist"); + return data[path].length; +} + +export const renameFile = data => async (oldKey, newKey) => { + if(!await exists(data)(oldKey)) throw new Error("cannot rename path: " + oldKey + " ... does not exist"); + if(await exists(data)(newKey)) throw new Error("cannot rename path: " + newKey + " ... already exists"); + data[newKey] = data[oldKey]; + delete data[oldKey]; +}; + +export const loadFile = data => async (path) => { + const result = data[path]; + if(isUndefined(result)) throw new Error("Load failed - path " + path + " does not exist"); + return result; +}; +export const exists = data => async (path) => has(data, path); +export const deleteFile = data => async (path) => { + if(!await exists(data)(path)) + throw new Error("Cannot delete file, path " + path + " does not exist"); + if(isFolder(data[path])) throw new Error("DeleteFile: Path " + path + " is a folder, not a file"); + const parentFolder = getParentFolder(data, path); + parentFolder.items = parentFolder.items.filter(i => i !== getLastPartInKey(path)); + data[getParentFolderKey(path)] = JSON.stringify(parentFolder); + delete data[path]; +} +export const createFolder = data => async (path) => { + if(await exists(data)(path)) throw new Error("Cannot create folder, path " + path + " already exists"); + addItemToParentFolder(data, path); + data[path] = JSON.stringify({folderMarker, items:[]}); +} +export const deleteFolder = data => async (path) => { + if(!await exists(data)(path)) throw new Error("Cannot delete folder, path " + path + " does not exist"); + if(!isFolder(data[path])) + throw new Error("DeleteFolder: Path " + path + " is not a folder"); + delete data[path]; +} + +export const getFolderContents = data => async (folderPath) => { + if(!isFolder(data[folderPath])) + throw new Error("Not a folder: " + folderPath); + if(!await exists(data)(folderPath)) + throw new Error("Folder does not exist: " + folderPath); + return JSON.parse(data[folderPath]).items; +}; + +export default data => { + return { + createFile : createFile(data), + updateFile : updateFile(data), + loadFile : loadFile(data), + exists : exists(data), + deleteFile : deleteFile(data), + createFolder: createFolder(data), + deleteFolder: deleteFolder(data), + readableFileStream: readableFileStream(data), + writableFileStream: writableFileStream(data), + renameFile: renameFile(data), + getFolderContents: getFolderContents(data), + getFileSize: getFileSize(data), + datastoreType : "memory", + datastoreDescription: "", + data + }; +}; \ No newline at end of file diff --git a/packages/core/test/nodeCrypto.js b/packages/core/test/nodeCrypto.js new file mode 100644 index 0000000000..94a9df4f99 --- /dev/null +++ b/packages/core/test/nodeCrypto.js @@ -0,0 +1,5 @@ +import {hash, verify} from "argon2"; + +export default { + hash, verify +}; \ No newline at end of file diff --git a/packages/core/test/recordApi.customId.spec.js b/packages/core/test/recordApi.customId.spec.js new file mode 100644 index 0000000000..837c64c394 --- /dev/null +++ b/packages/core/test/recordApi.customId.spec.js @@ -0,0 +1,34 @@ +import {setupApphierarchy, basicAppHierarchyCreator_WithFields, + getNewFieldAndAdd, stubEventHandler} from "./specHelpers"; +import { iterateIndex } from "../src/indexing/read"; + +describe("get customId", () => { + it("should generate an id with given value", async () => { + + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const customId = recordApi.customId("customer", "my_custom_id"); + expect(customId).toBe(`${appHierarchy.customerRecord.nodeId}-my_custom_id`); + + }); + + it("should throw error when nodeName does not exist", async () => { + + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + expect(() => recordApi.customId("not a node", "my_ custom_id")).toThrow(); + + }); + +}); + +describe("set customId", () => { + it("should generate custom id and set on given record", async () => { + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const customer = recordApi.getNew("/customers", "customer"); + + recordApi.setCustomId(customer, "my_custom_id"); + expect(customer.id).toBe(`${appHierarchy.customerRecord.nodeId}-my_custom_id`) + expect(customer.key).toBe(`/customers/${appHierarchy.customerRecord.nodeId}-my_custom_id`) + }) +}) + + diff --git a/packages/core/test/recordApi.delete.spec.js b/packages/core/test/recordApi.delete.spec.js new file mode 100644 index 0000000000..85ee5845d3 --- /dev/null +++ b/packages/core/test/recordApi.delete.spec.js @@ -0,0 +1,64 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import {keys, filter} from "lodash/fp"; +import {$} from "../src/common"; +import {permission} from "../src/authApi/permissions"; + +describe("recordApi > delete", () => { + + it("should remove every key in record's path", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + await recordApi.save(record); + await recordApi.delete(record.key); + + const remainingKeys = $(recordApi._storeHandle.data, [ + keys, + filter(k => k.startsWith(record.key)) + ]); + + expect(remainingKeys).toEqual([]); + + }); + + + it("should remove every key in record's path, when record has child records", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + await recordApi.save(record); + + const invoice = recordApi.getNew(`${record.key}/invoices`, "invoice"); + await recordApi.save(invoice); + + await recordApi.delete(record.key); + + const remainingKeys = $(recordApi._storeHandle.data, [ + keys, + filter(k => k.startsWith(record.key)) + ]); + + expect(remainingKeys).toEqual([]); + + }); + + it("should throw error when user user does not have permission", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + const created = await recordApi.save(record); + app.removePermission(permission.deleteRecord.get(appHierarchy.customerRecord.nodeKey())); + expect(recordApi.delete(created.key)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + const saved = await recordApi.save(record); + app.withOnlyThisPermission(permission.deleteRecord.get(appHierarchy.customerRecord.nodeKey())); + await recordApi.delete(saved.key); + }); + +}) diff --git a/packages/core/test/recordApi.files.spec.js b/packages/core/test/recordApi.files.spec.js new file mode 100644 index 0000000000..2aefa12b62 --- /dev/null +++ b/packages/core/test/recordApi.files.spec.js @@ -0,0 +1,98 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields} from "./specHelpers"; +import {keys, filter} from "lodash/fp"; +import {$} from "../src/common"; +import {permission} from "../src/authApi/permissions"; +import {Readable} from "readable-stream"; + + +const getFile = () => { + const contentString = "hello"; + var bytes = []; // char codes + + for (let i = 0; i < contentString.length; ++i) { + const code = contentString.charCodeAt(i); + bytes = bytes.concat([code & 0xff, code / 256 >>> 0]); + } + + const s = new Readable(); + s._read = () => { + s.push(Buffer.from(bytes)); + s.push(null); + }; + + return ({ + file: {relativePath:"thefile.txt", size:bytes.length}, + content: bytes, + stream: s + }); +} + + + +describe("recordApi > files", () => { + + it("upload should fail when files size does not equal stream size", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const {file, stream, content} = getFile(); + file.size = file.size - 1; + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.profilepic = file; + await recordApi.save(record); + expect(recordApi.uploadFile(record.key, stream, file.relativePath)).rejects.toThrow(); + }); + + it("upload should fail when record does not exist", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const {file, stream, content} = getFile(); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.profilepic = file; + await recordApi.save(record); + expect(recordApi.uploadFile("does nto exist", stream, file.relativePath)).rejects.toThrow(); + }); + + it("download should get an uploaded file", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const {file, stream, content} = getFile(); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.profilepic = file; + await recordApi.save(record); + await recordApi.uploadFile(record.key, stream, file.relativePath); + const downloadedStream = await recordApi.downloadFile(record.key, file.relativePath); + const downloadedBytes = downloadedStream.read(); + for(let i=0; i { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const {file, stream, content} = getFile(); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.profilepic = file; + await recordApi.save(record); + let ex; + try { + await recordApi.uploadFile(record.key, stream, "some:file.txt"); + } catch (e) { + ex = e; + } + expect(ex).not.toBeNull(); + }); + + + it("upload should fail when path contains '..' ", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const {file, stream, content} = getFile(); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.profilepic = file; + await recordApi.save(record); + expect(recordApi.uploadFile(record.key, stream, "../somefile.txt")).rejects.toThrow(); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/recordApi.getContext.spec.js b/packages/core/test/recordApi.getContext.spec.js new file mode 100644 index 0000000000..5b33a8cb8b --- /dev/null +++ b/packages/core/test/recordApi.getContext.spec.js @@ -0,0 +1,94 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields, getNewFieldAndAdd} from "./specHelpers"; +import {joinKey} from "../src/common"; +import {isFunction, isArray} from "lodash"; + +describe("recordApi > getContext", () => { + + it("'referenceExists()' should return true when the reference is in the index", async () => { + + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer.isalive = true; + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey(customer.key, "invoices"); + const invoice = recordApi.getNew(invoiceCollectionKey, "invoice"); + + const context = await recordApi.getContext(invoice.key); + + expect(isFunction(context.referenceExists)).toBeTruthy(); + const result = await context.referenceExists("customer", customer.key); + expect(result).toBe(true); + }); + + it("'referenceExists()' should return false when the reference is not in the index", async () => { + + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer.isalive = true; + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey(customer.key, "invoices"); + const invoice = recordApi.getNew(invoiceCollectionKey, "invoice"); + + const context = await recordApi.getContext(invoice.key); + + const result = await context.referenceExists("customer", "not a key"); + expect(result).toBe(false); + }); + + it("referenceOptions() should return list of indexed {key, value}", async () => { + + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer.surname = "Leedog"; + customer.isalive = true; + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey(customer.key, "invoices"); + const invoice = recordApi.getNew(invoiceCollectionKey, "invoice"); + + const context = await recordApi.getContext(invoice.key); + + expect(isFunction(context.referenceOptions)).toBeTruthy(); + const result = await context.referenceOptions("customer"); + expect(isArray(result)).toBeTruthy(); + expect(result[0].key).toBe(customer.key); + expect(result[0].value).toBe(customer.surname); + + }); + + it("referenceOptions() should return an empty list when no records are in the index", async () => { + + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew( + appHierarchy.customerRecord.collectionNodeKey(), + "customer"); + customer.surname = "Leedog"; + customer.isalive = false; + await recordApi.save(customer); + + const invoiceCollectionKey = joinKey(customer.key, "invoices"); + const invoice = recordApi.getNew(invoiceCollectionKey, "invoice"); + + const context = await recordApi.getContext(invoice.key); + + expect(isFunction(context.referenceOptions)).toBeTruthy(); + const result = await context.referenceOptions("customer"); + expect(isArray(result)).toBeTruthy(); + expect(result.length).toBe(0); + }); + +}); + diff --git a/packages/core/test/recordApi.reindex.spec.js b/packages/core/test/recordApi.reindex.spec.js new file mode 100644 index 0000000000..86a3648d8f --- /dev/null +++ b/packages/core/test/recordApi.reindex.spec.js @@ -0,0 +1,597 @@ +import {setupApphierarchy, + basicAppHierarchyCreator_WithFields, + basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers"; +import {joinKey} from "../src/common"; +import {some, isArray} from "lodash"; + +describe("recordApi > create > reindex", () => { + + it("should add to default index, when record created", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = true; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const items = await indexApi.listItems("/customer_index"); + + expect(items.length).toBe(1); + expect(items[0].surname).toBe("Ledog"); + expect(items[0].key).toBeDefined(); + expect(items[0].key).toEqual(record.key); + }); + + it("should add to index with filter, when record created and passes filter", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = false; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const items = await indexApi.listItems("/deceased"); + + expect(items.length).toBe(1); + expect(items[0].surname).toBe("Ledog"); + expect(items[0].key).toBeDefined(); + expect(items[0].key).toEqual(record.key); + }); + + it("should not add to index with filter, when record created and fails filter", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = true; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const items = await indexApi.listItems("/deceased"); + + expect(items.length).toBe(0); + }); + + + it("should be able to add to and list subcollection, after save (i.e. save initialiieses collection)", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew("/customers", "customer"); + await recordApi.save(customer); + + const invoicesCollectionKey = joinKey(customer.key, "invoices"); + const invoice = recordApi.getNew(invoicesCollectionKey, "invoice"); + invoice.totalIncVat = 10.5; + invoice.createdDate = new Date(); + await recordApi.save(invoice); + + const invoices = await indexApi.listItems( + joinKey(customer.key, "invoice_index")); + + expect(isArray(invoices)).toBeTruthy(); + expect(invoices.length).toBe(1); + expect(invoices[0].totalIncVat).toBe(10.5); + + }); + + it("should add to global index, when required", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.age = 9; + customer.isalive = true, + customer.createdDate = new Date(); + await recordApi.save(customer); + + const customers = await indexApi.listItems("/customersReference"); + + expect(isArray(customers)).toBeTruthy(); + expect(customers.length).toBe(1); + expect(customers[0].name).toBe("Ledog"); + }); + + it("should add to reverse reference index, when required", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const referredByCustomer = recordApi.getNew("/customers", "customer"); + referredByCustomer.surname = "Ledog"; + referredByCustomer.age = 9; + referredByCustomer.isalive = true, + referredByCustomer.createdDate = new Date(); + await recordApi.save(referredByCustomer); + + const referredCustomer = recordApi.getNew("/customers", "customer"); + referredCustomer.surname = "Zeecat"; + referredCustomer.age = 9; + referredCustomer.isalive = true, + referredCustomer.createdDate = new Date(); + referredCustomer.referredBy = { + key:referredByCustomer.key, + value:referredByCustomer.surname}; + await recordApi.save(referredCustomer); + + const customersReferredTo = await indexApi.listItems( + joinKey(referredByCustomer.key, "referredToCustomers") + ); + + expect(isArray(customersReferredTo)).toBeTruthy(); + expect(customersReferredTo.length).toBe(1); + expect(customersReferredTo[0].surname).toBe("Zeecat"); + }); + + it("should add to sharded index, when record created, and should add into correct shards", async () => { + + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + const items = await indexApi.listItems("/customersBySurname"); + + expect(items.length).toBe(2); + expect(items[0].surname).toBe("Ledog"); + expect(items[0].key).toEqual(record1.key); + + expect(items[1].surname).toBe("Zeecat"); + expect(items[1].key).toEqual(record2.key); + }); + +}); + +describe("recordApi > delete > reindex", () => { + + it("should remove from default index", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = true; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + await recordApi.delete(record.key); + + const itemsAfterDelete= await indexApi.listItems("/customer_index"); + expect(itemsAfterDelete.length).toBe(0); + }); + + + it("should remove from sharded index", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const record1 = recordApi.getNew("/customers", "customer"); + record1.surname = "Ledog"; + await recordApi.save(record1); + + const record2 = recordApi.getNew("/customers", "customer"); + record2.surname = "Zeecat"; + await recordApi.save(record2); + + await recordApi.delete(record1.key); + + const itemsAfterDelete= await indexApi.listItems("/customersBySurname"); + expect(itemsAfterDelete.length).toBe(1); + expect(itemsAfterDelete[0].surname).toBe("Zeecat"); + }); + + it("should remove from all indexes", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const referredBy = recordApi.getNew("/customers", "customer"); + referredBy.surname = "Zeecat"; + + await recordApi.save(referredBy); + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + record.isalive = false; + record.age = 9; + record.createddate = new Date(); + record.referredBy = { + key: referredBy.key, + value: referredBy.surname + }; + + await recordApi.save(record); + await recordApi.delete(record.key); + + const itemsAfterDelete= await indexApi.listItems("/customer_index"); + expect(itemsAfterDelete.length).toBe(1); + expect(itemsAfterDelete[0].surname).toBe("Zeecat"); + + const deceasedItemsAfterDelete= + await indexApi.listItems("/deceased"); + expect(deceasedItemsAfterDelete.length).toBe(0); + + const referredToItemsAfterDelete = + await indexApi.listItems(`${referredBy.key}/referredToCustomers`); + expect(referredToItemsAfterDelete.length).toBe(0); + + }); + + it("should only remove relevant record from all indexes", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = false; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const otherRecord = recordApi.getNew("/customers", "customer"); + otherRecord.surname = "Zeecat"; + otherRecord.isalive = false; + otherRecord.age = 12; + record.createddate = new Date(); + + await recordApi.save(otherRecord); + + await recordApi.delete(record.key); + + const itemsAfterDelete= await indexApi.listItems("/customer_index"); + expect(itemsAfterDelete.length).toBe(1); + expect(itemsAfterDelete[0].surname).toBe("Zeecat"); + + const deceasedItemsAfterDelete= + await indexApi.listItems("/deceased"); + expect(deceasedItemsAfterDelete.length).toBe(1); + expect(deceasedItemsAfterDelete[0].surname).toBe("Zeecat"); + }); + + it("should remove from global index", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.age = 9; + customer.isalive = true, + customer.createdDate = new Date(); + await recordApi.save(customer); + await recordApi.delete(customer.key); + const customers = await indexApi.listItems("/customersReference"); + + expect(isArray(customers)).toBeTruthy(); + expect(customers.length).toBe(0); + }); +}); + +describe("recordApi > update > reindex", () => { + + it("should update values in indexes", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = false; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const loadedRecord = await recordApi.load(record.key); + loadedRecord.surname = "Zeedog"; + await recordApi.save(loadedRecord); + + const itemsDefault = await indexApi.listItems("/customer_index"); + expect(itemsDefault[0].surname).toBe("Zeedog"); + expect(itemsDefault.length).toBe(1); + + }); + + it("should update values in sharded index", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + await recordApi.save(record); + + const loadedRecord = await recordApi.load(record.key); + loadedRecord.surname = "Zeedog"; + await recordApi.save(loadedRecord); + + const itemsDefault = await indexApi.listItems("/customersBySurname"); + expect(itemsDefault[0].surname).toBe("Zeedog"); + expect(itemsDefault.length).toBe(1); + + }); + + it("should only update values of relevant item", async () => { + const {recordApi, + indexApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = false; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const otherRecord = recordApi.getNew("/customers", "customer"); + otherRecord.surname = "Zeecat"; + otherRecord.isalive = false; + otherRecord.age = 12; + record.createddate = new Date(); + + await recordApi.save(otherRecord); + + const loadedRecord = await recordApi.load(record.key); + loadedRecord.surname = "Zeedog"; + await recordApi.save(loadedRecord); + + const items = await indexApi.listItems("/customer_index"); + + const hasItemWithSurname = sn => + some(items, i => i.surname === sn); + + expect(hasItemWithSurname("Zeedog")).toEqual(true); + expect(hasItemWithSurname("Ledog")).toEqual(false); + expect(hasItemWithSurname("Zeecat")).toEqual(true); + expect(items.length).toBe(2); + }); + + it("should update global index", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.age = 9; + customer.isalive = true, + customer.createdDate = new Date(); + await recordApi.save(customer); + + const loadedCustomer = await recordApi.load(customer.key); + loadedCustomer.surname = "Zeecat"; + await recordApi.save(loadedCustomer); + + const customers = await indexApi.listItems("/customersReference"); + expect(isArray(customers)).toBeTruthy(); + expect(customers.length).toBe(1); + expect(customers[0].name).toBe("Zeecat"); + }); + + it("should remove from one reference index and add to another when field changed", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const partner2 = recordApi.getNew("/partners", "partner"); + partner2.businessName = "Big Corp ltd"; + await recordApi.save(partner2); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + + const customerSaved = await recordApi.save(customer); + + customerSaved.partner = { + key: partner2.key, value: partner2.businessName + }; + + await recordApi.save(customerSaved); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + + const partner2Customer = + await indexApi.listItems(`${partner2.key}/partnerCustomers`); + expect(partner2Customer.length).toBe(1); + }); + + it("should remove from reference index when reference blanked", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + + const customerSaved = await recordApi.save(customer); + + customerSaved.partner = { + key: "", value: "" + }; + + await recordApi.save(customerSaved); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + }); + + it("should remove from reference index when filter no longer passes", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + + const customerSaved = await recordApi.save(customer); + + customerSaved.isalive = false; + + await recordApi.save(customerSaved); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + }); + + it("should not add to reference index when filter does not pass", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + customer.isalive = false; + + await recordApi.save(customer); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + }); + + + it("should remove from reference index, and not re-added when no longer passes filter, but reference is changed", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const partner2 = recordApi.getNew("/partners", "partner"); + partner2.businessName = "Big Corp ltd"; + await recordApi.save(partner2); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + + const customerSaved = await recordApi.save(customer); + + customerSaved.partner = { + key: partner2.key, value: partner2.businessName + }; + customerSaved.isalive = false; + + await recordApi.save(customerSaved); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + + const partner2Customer = + await indexApi.listItems(`${partner2.key}/partnerCustomers`); + expect(partner2Customer.length).toBe(0); + }); + + it("should add to reference index, when reference is changed, and did not previsouly pass filter", async () => { + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + await recordApi.save(partner1); + + const partner2 = recordApi.getNew("/partners", "partner"); + partner2.businessName = "Big Corp ltd"; + await recordApi.save(partner2); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + customer.isalive = false; + + const customerSaved = await recordApi.save(customer); + + customerSaved.partner = { + key: partner2.key, value: partner2.businessName + }; + customerSaved.isalive = true; + + await recordApi.save(customerSaved); + + const partner1Customer = + await indexApi.listItems(`${partner1.key}/partnerCustomers`); + expect(partner1Customer.length).toBe(0); + + const partner2Customer = + await indexApi.listItems(`${partner2.key}/partnerCustomers`); + expect(partner2Customer.length).toBe(1); + }); + +}); + +describe("referenced object changed", () => { + + it("should update the reference", async () => { + + const {recordApi, indexApi} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const partner1 = recordApi.getNew("/partners", "partner"); + partner1.businessName = "ACME inc"; + const savedPartner = await recordApi.save(partner1); + + const customer = recordApi.getNew("/customers", "customer"); + customer.surname = "Ledog"; + customer.partner = { + key: partner1.key, value: partner1.businessName + }; + await recordApi.save(customer); + savedPartner.businessName = "A.C.M.E Inc"; + await recordApi.save(savedPartner); + + const updatedCustomer = await recordApi.load(customer.key); + + expect(updatedCustomer.partner.name).toBe(savedPartner.businessName); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/recordApi.spec.js b/packages/core/test/recordApi.spec.js new file mode 100644 index 0000000000..724ba22585 --- /dev/null +++ b/packages/core/test/recordApi.spec.js @@ -0,0 +1,348 @@ +import {setupApphierarchy, basicAppHierarchyCreator_WithFields, + getNewFieldAndAdd, stubEventHandler} from "./specHelpers"; +import {events, isNonEmptyString} from "../src/common"; +import { isBoolean } from "util"; +import {permission} from "../src/authApi/permissions"; + +describe("recordApi > getNew", () => { + + it("should get object with generated id and key (full path)", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + expect(record.id).toBeDefined(); + expect(isNonEmptyString(record.id)).toBeTruthy(); + + expect(record.key).toBeDefined(); + expect(isNonEmptyString(record.key)).toBeTruthy(); + expect(record.key).toBe(`/customers/${record.id}`); + }); + + it("should create object with all declared fields, using default values", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + + const newRecord = recordApi.getNew("/customers", "customer") + + expect(newRecord.surname).toBe(null); + expect(newRecord.isalive).toBe(true); + expect(newRecord.createddate).toBe(null); + expect(newRecord.age).toBe(null); + }); + + it("should create object with all declared fields, and use inital values", async () => { + const {recordApi} = await setupApphierarchy(templateApi => { + const hierarchy = basicAppHierarchyCreator_WithFields(templateApi); + const {root, customerRecord} = hierarchy; + + customerRecord.fields = []; + + const newField = getNewFieldAndAdd(templateApi, customerRecord); + newField("surname", "string", "hello"); + newField("isalive", "bool", "true"); + newField("age", "number", "999"); + + return hierarchy; + }); + + const newRecord = recordApi.getNew("/customers", "customer") + + expect(newRecord.surname).toBe("hello"); + expect(newRecord.isalive).toBe(true); + expect(newRecord.age).toBe(999); + }); + + it("should add a function 'isNew' which always returns true", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + expect(record.isNew).toBeDefined(); + expect(isBoolean(record.isNew)).toBeTruthy(); + expect(record.isNew).toBeTruthy(); + }); + + it("should add a function 'type' returns type", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + expect(record.type).toBeDefined(); + expect(isNonEmptyString(record.type)).toBeTruthy(); + expect(record.type).toBe("customer"); + }); + + it("should throw error, user user does not have permission", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.removePermission(permission.createRecord.get(appHierarchy.customerRecord.nodeKey())); + expect(() => recordApi.getNew("/customers", "customer")).toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withOnlyThisPermission(permission.createRecord.get(appHierarchy.customerRecord.nodeKey())); + recordApi.getNew("/customers", "customer"); + }); +}); + +describe('recordApi > save then load', () => { + + it('should save all field values on create new record', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = true; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + expect(saved.surname).toBe(record.surname); + expect(saved.isalive).toBe(record.isalive); + expect(saved.age).toBe(record.age); + expect(saved.createddate).toEqual(record.createddate); + + }); + + it('should create values for fields when undefined', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + expect(saved.surname).toBe(null); + expect(saved.isalive).toBe(true); + expect(saved.age).toBe(record.age); + expect(saved.createddate).toEqual(record.createddate); + + }); + + it('loaded record isNew() always return false', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + expect(saved.isNew).toBeDefined(); + expect(saved.isNew).toBe(false); + }); + + it('loaded record id() and key() should work', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + expect(saved.id).toBeDefined(); + expect(saved.id).toBe(record.id); + + expect(saved.key).toBeDefined(); + expect(saved.key).toBe(saved.key); + }); + + + it('loaded record type() should be a function', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + expect(isNonEmptyString(saved.type)).toBeTruthy() + expect(saved.type).toBe("customer"); + }); + + it('update existing should update field', async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = true; + record.age = 9; + record.createddate = new Date(); + + await recordApi.save(record); + + const saved = await recordApi.load(record.key); + + saved.surname = "Zeedog"; + await recordApi.save(saved); + const savedAgain = await recordApi.load(saved.key); + expect(savedAgain.surname).toBe(saved.surname); + }); +}); + +describe("save", () => { + + it("IsNew() should return false after save", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + expect(savedRecord.isNew).toBe(false); + }); + + it("should publish onbegin and oncomplete events", async () => { + const {recordApi, subscribe} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const handler = stubEventHandler(); + subscribe(events.recordApi.save.onBegin, handler.handle); + subscribe(events.recordApi.save.onComplete, handler.handle); + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + await recordApi.save(record); + + const onBegin = handler.getEvents(events.recordApi.save.onBegin); + const onComplete = handler.getEvents(events.recordApi.save.onComplete); + expect(onBegin.length).toBe(1); + expect(onComplete.length).toBe(1); + expect(onBegin[0].context.record).toBeDefined(); + expect(onBegin[0].context.record.key).toBe(record.key); + expect(onComplete[0].context.record).toBeDefined(); + expect(onComplete[0].context.record.key).toBe(record.key); + + }); + + it("should publish create on create and update on update", async () => { + const {recordApi, subscribe} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const handler = stubEventHandler(); + subscribe(events.recordApi.save.onRecordCreated, handler.handle); + subscribe(events.recordApi.save.onRecordUpdated, handler.handle); + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord =await recordApi.save(record); + const onCreate = handler.getEvents(events.recordApi.save.onRecordCreated); + expect(onCreate.length).toBe(1); + expect(onCreate[0].context.record).toBeDefined(); + expect(onCreate[0].context.record.key).toBe(record.key); + + savedRecord.surname = "Zeecat"; + await recordApi.save(savedRecord); + + const onUpdate = handler.getEvents(events.recordApi.save.onRecordUpdated); + expect(onUpdate.length).toBe(1); + expect(onUpdate[0].context.old).toBeDefined(); + expect(onUpdate[0].context.old.key).toBe(record.key); + expect(onUpdate[0].context.old.surname).toBe("Ledog"); + expect(onUpdate[0].context.new).toBeDefined(); + expect(onUpdate[0].context.new.key).toBe(record.key); + expect(onUpdate[0].context.new.surname).toBe("Zeecat"); + + }); + + it("should create folder and index for subcollection", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + expect(await recordApi._storeHandle.exists(`${record.key}/invoice_index/index.csv`)).toBeTruthy() + expect(await recordApi._storeHandle.exists(`${record.key}/invoice_index`)).toBeTruthy() + expect(await recordApi._storeHandle.exists(`${record.key}/invoices`)).toBeTruthy() + }); + + it("should create index folder and shardMap for sharded reverse reference index", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + await recordApi.save(record); + expect(await recordApi._storeHandle.exists(`${record.key}/referredToCustomers/shardMap.json`)).toBeTruthy(); + expect(await recordApi._storeHandle.exists(`${record.key}/referredToCustomers`)).toBeTruthy(); + }); + + it("should create folder for record", async () => { + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + const savedRecord = await recordApi.save(record); + expect(await recordApi._storeHandle.exists(`${record.key}`)).toBeTruthy(); + expect(await recordApi._storeHandle.exists(`${record.key}/record.json`)).toBeTruthy(); + }); + + it("should create allids file", async () => { + const {recordApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + record.surname = "Ledog"; + + await recordApi.save(record); + + const allIdsPath = `/customers/allids/${appHierarchy.customerRecord.nodeId}/${record.id[2]}`; + expect(await recordApi._storeHandle.exists(allIdsPath)).toBeTruthy(); + + }); + + it("create should throw error, user user does not have permission", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + app.removePermission(permission.createRecord.get(appHierarchy.customerRecord.nodeKey())); + expect(recordApi.save(record)).rejects.toThrow(/Unauthorized/); + }); + + it("create should not depend on having any other permissions", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + app.withOnlyThisPermission(permission.createRecord.get(appHierarchy.customerRecord.nodeKey())); + const record = recordApi.getNew("/customers", "customer"); + await recordApi.save(record); + }); + + it("update should throw error, user user does not have permission", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + app.removePermission(permission.updateRecord.get(appHierarchy.customerRecord.nodeKey())); + const created = await recordApi.save(record); + expect(recordApi.save(created)).rejects.toThrow(/Unauthorized/); + }); + + it("update should not depend on having any other permissions", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + const saved = await recordApi.save(record); + app.withOnlyThisPermission(permission.updateRecord.get(appHierarchy.customerRecord.nodeKey())); + await recordApi.save(saved); + }); +}) + +describe("recordApi > load", () => { + + it("should throw error when user user does not have permission", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + const created = await recordApi.save(record); + app.removePermission(permission.readRecord.get(appHierarchy.customerRecord.nodeKey())); + expect(recordApi.load(created.key)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {recordApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + const saved = await recordApi.save(record); + app.withOnlyThisPermission(permission.readRecord.get(appHierarchy.customerRecord.nodeKey())); + await recordApi.load(saved.key); + }); + +}); diff --git a/packages/core/test/recordApi.validate.spec.js b/packages/core/test/recordApi.validate.spec.js new file mode 100644 index 0000000000..a4b74903c1 --- /dev/null +++ b/packages/core/test/recordApi.validate.spec.js @@ -0,0 +1,268 @@ +import {setupApphierarchy, stubEventHandler, + basicAppHierarchyCreator_WithFields, basicAppHierarchyCreator_WithFields_AndIndexes, + hierarchyFactory, withFields} from "./specHelpers"; +import {find} from "lodash"; +import {addHours} from "date-fns"; +import {events} from "../src/common" + +describe("recordApi > validate", () => { + + it("should return errors when any fields do not parse", async () => { + + const {recordApi} = await setupApphierarchy(basicAppHierarchyCreator_WithFields); + const record = recordApi.getNew("/customers", "customer"); + + record.surname = "Ledog"; + record.isalive = "hello"; + record.age = "nine"; + record.createddate = "blah"; + + const validationResult = await recordApi.validate(record); + + expect(validationResult.isValid).toBe(false); + expect(validationResult.errors.length).toBe(3); + }); + + it("should return errors when mandatory field is empty", async () => { + const withValidationRule = (hierarchy, templateApi) => { + templateApi.addRecordValidationRule(hierarchy.customerRecord) + (templateApi.commonRecordValidationRules.fieldNotEmpty("surname")); + }; + + const hierarchyCreator = hierarchyFactory(withFields, withValidationRule); + const {recordApi} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + + record.surname = ""; + + const validationResult = await recordApi.validate(record); + + expect(validationResult.isValid).toBe(false); + expect(validationResult.errors.length).toBe(1); + }); + + it("should return error when string field is beyond maxLength", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const surname = find(hierarchy.customerRecord.fields, f => f.name === "surname"); + surname.typeOptions.maxLength = 5; + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "more than 5 chars"; + + const validationResult = await recordApi.validate(record); + expect(validationResult.isValid).toBe(false); + expect(validationResult.errors.length).toBe(1); + }); + + it("should return error when number field is > maxValue", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const age = find(hierarchy.customerRecord.fields, f => f.name === "age"); + age.typeOptions.maxValue = 10; + age.typeOptions.minValue = 5 + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const tooOldRecord = recordApi.getNew("/customers", "customer"); + tooOldRecord.age = 11 + + const tooOldResult = await recordApi.validate(tooOldRecord); + expect(tooOldResult.isValid).toBe(false); + expect(tooOldResult.errors.length).toBe(1); + + }); + + it("should return error when number field is < minValue", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const age = find(hierarchy.customerRecord.fields, f => f.name === "age"); + age.typeOptions.minValue = 5 + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + const tooYoungRecord = recordApi.getNew("/customers", "customer"); + tooYoungRecord.age = 3 + + const tooYoungResult = await recordApi.validate(tooYoungRecord); + expect(tooYoungResult.isValid).toBe(false); + expect(tooYoungResult.errors.length).toBe(1); + }); + + it("should return error when number has too many decimal places", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const age = find(hierarchy.customerRecord.fields, f => f.name === "age"); + age.typeOptions.decimalPlaces = 2; + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + const record = recordApi.getNew("/customers", "customer"); + record.age = 3.123 + + const validationResult = await recordApi.validate(record); + expect(validationResult.isValid).toBe(false); + expect(validationResult.errors.length).toBe(1); + }); + + it("should return error when datetime field is > maxValue", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const createddate = find(hierarchy.customerRecord.fields, f => f.name === "createddate"); + createddate.typeOptions.maxValue = new Date(); + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.createddate = addHours(new Date(), 1); + + const result = await recordApi.validate(record); + expect(result.isValid).toBe(false); + expect(result.errors.length).toBe(1); + + }); + + it("should return error when number field is < minValue", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const createddate = find(hierarchy.customerRecord.fields, f => f.name === "createddate"); + createddate.typeOptions.minValue = addHours(new Date(), 1); + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.createddate = new Date(); + + const result = await recordApi.validate(record); + expect(result.isValid).toBe(false); + expect(result.errors.length).toBe(1); + }); + + it("should return error when string IS NOT one of declared values, and only declared values are allowed", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const surname = find(hierarchy.customerRecord.fields, f => f.name === "surname"); + surname.typeOptions.allowDeclaredValuesOnly = true + surname.typeOptions.values = ["thedog"]; + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "zeecat"; + + const result = await recordApi.validate(record); + expect(result.isValid).toBe(false); + expect(result.errors.length).toBe(1); + }); + + it("should not return error when string IS one of declared values, and only declared values are allowed", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const surname = find(hierarchy.customerRecord.fields, f => f.name === "surname"); + surname.typeOptions.allowDeclaredValuesOnly = true + surname.typeOptions.values = ["thedog"]; + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "thedog"; + + const result = await recordApi.validate(record); + expect(result.isValid).toBe(true); + expect(result.errors.length).toBe(0); + }); + + it("should not return error when string IS NOT one of declared values, but any values are allowed", async () => { + const withFieldWithMaxLength = (hierarchy, templateApi) => { + const surname = find(hierarchy.customerRecord.fields, f => f.name === "surname"); + surname.typeOptions.allowDeclaredValuesOnly = false + surname.typeOptions.values = ["thedog"]; + }; + + const hierarchyCreator = hierarchyFactory(withFields, withFieldWithMaxLength); + const {recordApi, appHierarchy} = + await setupApphierarchy(hierarchyCreator); + + + const record = recordApi.getNew("/customers", "customer"); + record.surname = "zeecat"; + + const result = await recordApi.validate(record); + expect(result.isValid).toBe(true); + expect(result.errors.length).toBe(0); + }); + + it("should return error when reference field does not exist in options index", async () => { + const {recordApi, appHierarchy} = + await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes); + + const partner = recordApi.getNew("/partners", "partner"); + partner.businessName = "ACME Inc"; + await recordApi.save(partner); + + const customer = recordApi.getNew("/customers", "customer"); + customer.partner = {key: "incorrect key", name: partner.businessName}; + const result = await await recordApi.validate(customer); + expect(result.isValid).toBe(false); + expect(result.errors.length).toBe(1); + + }); + + it("should publish invalid events", async () => { + const withValidationRule = (hierarchy, templateApi) => { + templateApi.addRecordValidationRule(hierarchy.customerRecord) + (templateApi.commonRecordValidationRules.fieldNotEmpty("surname")); + }; + + const hierarchyCreator = hierarchyFactory(withFields, withValidationRule); + + const {recordApi, subscribe} = await setupApphierarchy(hierarchyCreator); + const handler = stubEventHandler(); + subscribe(events.recordApi.save.onInvalid, + handler.handle); + + const record = recordApi.getNew("/customers", "customer"); + record.surname = ""; + + try{ + await recordApi.save(record); + } catch(e) + {} + + const onInvalid = handler.getEvents( + events.recordApi.save.onInvalid + ); + expect(onInvalid.length).toBe(1); + expect(onInvalid[0].context.record).toBeDefined(); + expect(onInvalid[0].context.record.key).toBe(record.key); + expect(onInvalid[0].context.validationResult).toBeDefined(); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/specHelpers.js b/packages/core/test/specHelpers.js new file mode 100644 index 0000000000..ade93c8b3d --- /dev/null +++ b/packages/core/test/specHelpers.js @@ -0,0 +1,507 @@ +import path from "path"; +import {getAppApis, getRecordApi, + getCollectionApi, getIndexApi, getActionsApi} from "../src"; +import memory from "./memory"; +import {setupDatastore} from "../src/appInitialise"; +import {configFolder, fieldDefinitions, + templateDefinitions, isNothing, + joinKey, + isSomething} from "../src/common"; +import { getNewIndexTemplate } from "../src/templateApi/createNodes"; +import {indexTypes} from "../src/templateApi/indexes"; +import getTemplateApi from "../src/templateApi"; +import {getApplicationDefinition} from "../src/templateApi/getApplicationDefinition"; +import getAuthApi from "../src/authApi"; +import {createEventAggregator} from "../src/appInitialise/eventAggregator"; +import {filter, find} from "lodash/fp"; +import {createBehaviourSources} from "../src/actionsApi/buildBehaviourSource"; +import {createAction, createTrigger} from "../src/templateApi/createActions"; +import {initialiseActions} from "../src/actionsApi/initialise"; +import {cleanup} from "../src/transactions/cleanup"; +import nodeCrypto from "./nodeCrypto"; +import {permission} from "../src/authApi/permissions"; +import {generateFullPermissions} from "../src/authApi/generateFullPermissions" +import {initialiseData} from "../src/appInitialise/initialiseData"; + +const exp = module.exports; + +export const testFileArea = (testNameArea) => path.join("test", "fs_test_area", testNameArea); +export const testConfigFolder = (testAreaName) => path.join(exp.testFileArea(testAreaName), configFolder); +export const testFieldDefinitionsPath = (testAreaName) => path.join(exp.testFileArea(testAreaName), fieldDefinitions); +export const testTemplatesPath = (testAreaName) => path.join(exp.testFileArea(testAreaName), templateDefinitions); + +export const getMemoryStore = () => setupDatastore(memory({})); +export const getMemoryTemplateApi = () => { + const app = { + datastore:getMemoryStore(), + publish: () => {}, + getEpochTime : async () => (new Date()).getTime(), + user:{name:"", permissions:[permission.writeTemplates.get()]} + }; + app.removePermission = removePermission(app); + app.withOnlyThisPermission = withOnlyThisPermission(app); + app.withNoPermissions = withNoPermissions(app); + const templateApi = getTemplateApi(app); + templateApi._eventAggregator = createEventAggregator(); + templateApi._storeHandle = app.datastore; + return {templateApi, app}; +} + +// TODO: subscribe actions +export const appFromTempalteApi = async (templateApi, disableCleanupTransactions=false) => { + const appDef = await templateApi.getApplicationDefinition(); + const app = { + hierarchy:appDef.hierarchy, + datastore:templateApi._storeHandle, + publish:templateApi._eventAggregator.publish, + _eventAggregator: templateApi._eventAggregator, + getEpochTime : async () => (new Date()).getTime(), + crypto:nodeCrypto, + user:{name:"bob", permissions: []}, + actions:{} + }; + app.removePermission = removePermission(app); + app.withOnlyThisPermission = withOnlyThisPermission(app); + app.withNoPermissions = withNoPermissions(app); + + const fullPermissions = generateFullPermissions(app); + app.user.permissions = fullPermissions; + + if(disableCleanupTransactions) + app.cleanupTransactions = async () => {}; + else + app.cleanupTransactions = async () => await cleanup(app); + return app; +}; + +const removePermission = app => perm => { + app.user.permissions = filter(p => p.type !== perm.type + || (isSomething(perm.nodeKey) + && perm.nodeKey !== p.nodeKey)) + (app.user.permissions); +} + +const withOnlyThisPermission = app => perm => + app.user.permissions = [perm]; + +const withNoPermissions = app => () => + app.user.permissions = []; + +export const getRecordApiFromTemplateApi = async (templateApi, disableCleanupTransactions=false) => { + const app = await appFromTempalteApi(templateApi, disableCleanupTransactions); + const recordapi = getRecordApi(); + recordapi._storeHandle = app.datastore; +} + +export const getCollectionApiFromTemplateApi = async (templateApi, disableCleanupTransactions=false) => + getCollectionApi(await appFromTempalteApi(templateApi, disableCleanupTransactions)); + +export const getIndexApiFromTemplateApi = async (templateApi, disableCleanupTransactions=false) => + getIndexApi(await appFromTempalteApi(templateApi, disableCleanupTransactions)); + +export const getAuthApiFromTemplateApi = async (templateApi, disableCleanupTransactions=false) => + getAuthApi(await appFromTempalteApi(templateApi, disableCleanupTransactions)); + +export const findIndex = (parentNode, name) => + find(i => i.name === name)(parentNode.indexes); + +export const findCollectionDefaultIndex = (recordCollectionNode) => + findIndex(recordCollectionNode.parent(), recordCollectionNode.name + "_index"); + +export const hierarchyFactory = (...additionalFeatures) => templateApi => { + const root = templateApi.getNewRootLevel(); + + const settingsRecord = templateApi.getNewSingleRecordTemplate(root); + settingsRecord.name = "settings"; + + const customerRecord = templateApi.getNewRecordTemplate(root, "customer"); + customerRecord.collectionName = "customers"; + findCollectionDefaultIndex(customerRecord).map = "return {surname:record.surname, isalive:record.isalive};"; + + const partnerRecord = templateApi.getNewRecordTemplate(root, "partner"); + partnerRecord.collectionName = "partners"; + + const partnerInvoiceRecord = templateApi.getNewRecordTemplate(partnerRecord, "invoice"); + partnerInvoiceRecord.collectionName = "invoices"; + findCollectionDefaultIndex(partnerInvoiceRecord).name = "partnerInvoices_index"; + + const invoiceRecord = templateApi.getNewRecordTemplate(customerRecord, "invoice"); + invoiceRecord.collectionName = "invoices"; + findCollectionDefaultIndex(invoiceRecord).map = "return {createdDate: record.createdDate, totalIncVat: record.totalIncVat};"; + + const chargeRecord = templateApi.getNewRecordTemplate(invoiceRecord, "charge"); + chargeRecord.collectionName = "charges"; + + const hierarchy = ({root, customerRecord, + invoiceRecord, partnerRecord, + partnerInvoiceRecord, chargeRecord, settingsRecord}); + + for(let feature of additionalFeatures) { + feature(hierarchy, templateApi); + } + return hierarchy; +}; + +export const basicAppHierarchyCreator = templateApis => + hierarchyFactory()(templateApis); + +export const withFields = (hierarchy, templateApi) => { + const {customerRecord, invoiceRecord, + partnerInvoiceRecord, chargeRecord, + partnerRecord, settingsRecord, root} = hierarchy; + + getNewFieldAndAdd(templateApi, settingsRecord)("appName", "string", ""); + + const newCustomerField = getNewFieldAndAdd(templateApi, customerRecord); + + + const partnersReferenceIndex = templateApi.getNewIndexTemplate(root); + partnersReferenceIndex.name = "partnersReference"; + partnersReferenceIndex.map = "return {name:record.businessName};"; + partnersReferenceIndex.allowedRecordNodeIds = [partnerRecord.nodeId]; + + const partnerCustomersReverseIndex = templateApi.getNewIndexTemplate(partnerRecord, indexTypes.reference); + partnerCustomersReverseIndex.name = "partnerCustomers"; + partnerCustomersReverseIndex.map = "return {...record};"; + partnerCustomersReverseIndex.filter = "record.isalive === true" + partnerCustomersReverseIndex.allowedRecordNodeIds = [customerRecord.nodeId]; + hierarchy.partnerCustomersReverseIndex = partnerCustomersReverseIndex; + + newCustomerField("surname", "string"); + newCustomerField("isalive", "bool", "true"); + newCustomerField("createddate", "datetime"); + newCustomerField("age", "number"); + newCustomerField("profilepic", "file"); + const customerPartnerField = newCustomerField("partner", "reference", undefined, { + indexNodeKey : "/partnersReference", + displayValue : "name", + reverseIndexNodeKeys : [joinKey( + partnerRecord.nodeKey(), "partnerCustomers" )] + }); + + const referredToCustomersReverseIndex = templateApi.getNewIndexTemplate(customerRecord, indexTypes.reference); + referredToCustomersReverseIndex.name = "referredToCustomers"; + referredToCustomersReverseIndex.map = "return {...record};"; + referredToCustomersReverseIndex.getShardName = "return !record.surname ? 'null' : record.surname.substring(0,1);" + referredToCustomersReverseIndex.allowedRecordNodeIds = [customerRecord.nodeId]; + hierarchy.referredToCustomersReverseIndex = referredToCustomersReverseIndex; + + const customerReferredByField = newCustomerField("referredBy", "reference", undefined, { + indexNodeKey : "/customer_index", + displayValue : "surname", + reverseIndexNodeKeys : [joinKey( + customerRecord.nodeKey(), "referredToCustomers")] + }); + hierarchy.customerReferredByField = customerReferredByField; + + const newInvoiceField = getNewFieldAndAdd(templateApi, invoiceRecord); + + const invoiceTotalIncVatField = newInvoiceField("totalIncVat", "number"); + invoiceTotalIncVatField.typeOptions.decimalPlaces = 2; + newInvoiceField("createdDate", "datetime"); + newInvoiceField("paidAmount", "number"); + newInvoiceField("invoiceType", "string"); + newInvoiceField("isWrittenOff", "bool"); + + const newPartnerField = getNewFieldAndAdd(templateApi, partnerRecord); + newPartnerField("businessName", "string"); + + const newPartnerInvoiceField = getNewFieldAndAdd(templateApi, partnerInvoiceRecord); + const partnerInvoiceTotalIncVatVield = newPartnerInvoiceField("totalIncVat", "number"); + partnerInvoiceTotalIncVatVield.typeOptions.decimalPlaces = 2; + newPartnerInvoiceField("createdDate", "datetime"); + newPartnerInvoiceField("paidAmount", "number"); + + const newChargeField = getNewFieldAndAdd(templateApi, chargeRecord); + newChargeField("amount", "number"); + + const chargePartnerInvoiceField = newChargeField("partnerInvoice", "reference", undefined, { + reverseIndexNodeKeys : [joinKey( + partnerInvoiceRecord.nodeKey(), "partnerCharges" + )], + displayValue : "createdDate", + indexNodeKey : joinKey( + partnerRecord.nodeKey(), "partnerInvoices_index") + }); + + const partnerChargesReverseIndex = templateApi.getNewIndexTemplate(partnerInvoiceRecord, indexTypes.reference); + partnerChargesReverseIndex.name = "partnerCharges"; + partnerChargesReverseIndex.map = "return {...record};"; + partnerChargesReverseIndex.allowedRecordNodeIds = [chargeRecord]; + hierarchy.partnerChargesReverseIndex = partnerChargesReverseIndex; + + const customersReferenceIndex = templateApi.getNewIndexTemplate(hierarchy.root); + customersReferenceIndex.name = "customersReference"; + customersReferenceIndex.map = "return {name:record.surname}"; + customersReferenceIndex.filter = "record.isalive === true"; + customersReferenceIndex.allowedRecordNodeIds = [customerRecord.nodeId]; + + const invoiceCustomerField = newInvoiceField("customer", "reference", undefined, { + indexNodeKey : "/customersReference", + reverseIndexNodeKeys : [findCollectionDefaultIndex(invoiceRecord).nodeKey()], + displayValue : "name" + }); +} + +export const withIndexes = (hierarchy, templateApi) => { + const {root, customerRecord, + partnerInvoiceRecord, invoiceRecord, + partnerRecord, chargeRecord } = hierarchy; + const deceasedCustomersIndex = getNewIndexTemplate(root); + deceasedCustomersIndex.name = "deceased"; + deceasedCustomersIndex.map = "return {surname: record.surname, age:record.age};"; + deceasedCustomersIndex.filter = "record.isalive === false"; + findCollectionDefaultIndex(customerRecord).map = "return record;" + deceasedCustomersIndex.allowedRecordNodeIds = [customerRecord.nodeId]; + + findCollectionDefaultIndex(invoiceRecord).allowedRecordNodeIds = [invoiceRecord.nodeId]; + findCollectionDefaultIndex(customerRecord).allowedRecordNodeIds = [customerRecord.nodeId]; + findCollectionDefaultIndex(partnerRecord).allowedRecordNodeIds = [partnerRecord.nodeId]; + findIndex(partnerRecord, "partnerInvoices_index").allowedRecordNodeIds = [partnerInvoiceRecord.nodeId]; + findCollectionDefaultIndex(chargeRecord).allowedRecordNodeIds = [chargeRecord.nodeId]; + + const customerInvoicesIndex = getNewIndexTemplate(root); + customerInvoicesIndex.name = "customer_invoices"; + customerInvoicesIndex.map = "return record;"; + customerInvoicesIndex.filter = "record.type === 'invoice'"; + customerInvoicesIndex.allowedRecordNodeIds = [invoiceRecord.nodeId]; + + const outstandingInvoicesIndex = getNewIndexTemplate(root); + outstandingInvoicesIndex.name = "Outstanding Invoices"; + outstandingInvoicesIndex.filter = "record.type === 'invoice' && record.paidAmount < record.totalIncVat"; + outstandingInvoicesIndex.map = "return {...record};"; + outstandingInvoicesIndex.allowedRecordNodeIds = [ + invoiceRecord.nodeId, partnerInvoiceRecord.nodeId + ]; + + const allInvoicesAggregateGroup = templateApi.getNewAggregateGroupTemplate(outstandingInvoicesIndex); + allInvoicesAggregateGroup.name = "all_invoices"; + + const allInvoicesByType = templateApi.getNewAggregateGroupTemplate(outstandingInvoicesIndex); + allInvoicesByType.groupBy = "return record.invoiceType"; + allInvoicesByType.name = "all_invoices_by_type"; + + const allInvoicesTotalAmountAggregate = templateApi.getNewAggregateTemplate(allInvoicesByType); + allInvoicesTotalAmountAggregate.name = "totalIncVat"; + allInvoicesTotalAmountAggregate.aggregatedValue = "return record.totalIncVat"; + + const allInvoicesPaidAmountAggregate = templateApi.getNewAggregateTemplate(allInvoicesByType); + allInvoicesPaidAmountAggregate.name = "paidAmount"; + allInvoicesPaidAmountAggregate.aggregatedValue = "return record.paidAmount"; + + const writtenOffInvoicesByType = templateApi.getNewAggregateGroupTemplate(outstandingInvoicesIndex); + writtenOffInvoicesByType.groupBy = "return record.invoiceType"; + writtenOffInvoicesByType.name = "written_off"; + writtenOffInvoicesByType.condition = "record.isWrittenOff === true"; + + const writtenOffInvoicesTotalAmountAggregate = templateApi.getNewAggregateTemplate(writtenOffInvoicesByType); + writtenOffInvoicesTotalAmountAggregate.name = "totalIncVat"; + writtenOffInvoicesTotalAmountAggregate.aggregatedValue = "return record.totalIncVat"; + + const customersBySurnameIndex = templateApi.getNewIndexTemplate(root); + customersBySurnameIndex.name = "customersBySurname"; + customersBySurnameIndex.map = "return {...record};" + customersBySurnameIndex.filter = ""; + customersBySurnameIndex.allowedRecordNodeIds = [customerRecord.nodeId]; + customersBySurnameIndex.getShardName = "return !record.surname ? 'null' : record.surname.substring(0,1);" + + const customersDefaultIndex = findCollectionDefaultIndex(customerRecord); + const customersNoGroupaggregateGroup = templateApi.getNewAggregateGroupTemplate(customersDefaultIndex); + customersNoGroupaggregateGroup.name = "Customers Summary"; + const allCustomersAgeFunctions = templateApi.getNewAggregateTemplate(customersNoGroupaggregateGroup); + allCustomersAgeFunctions.aggregatedValue = "return record.age"; + allCustomersAgeFunctions.name = "all customers - age breakdown"; + + const invoicesByOutstandingIndex = templateApi.getNewIndexTemplate(customerRecord); + invoicesByOutstandingIndex.name = "invoicesByOutstanding"; + invoicesByOutstandingIndex.map = "return {...record};" + invoicesByOutstandingIndex.filter = ""; + invoicesByOutstandingIndex.getShardName = "return (record.totalIncVat > record.paidAmount ? 'outstanding' : 'paid');" + invoicesByOutstandingIndex.allowedRecordNodeIds = [ + partnerInvoiceRecord.nodeId, invoiceRecord.nodeId + ]; + const allInvoicesByType_Sharded = templateApi.getNewAggregateGroupTemplate(invoicesByOutstandingIndex); + allInvoicesByType_Sharded.groupBy = "return record.invoiceType"; + allInvoicesByType_Sharded.name = "all_invoices_by_type"; + + const allInvoicesTotalAmountAggregate_Sharded = templateApi.getNewAggregateTemplate(allInvoicesByType_Sharded); + allInvoicesTotalAmountAggregate_Sharded.name = "totalIncVat"; + allInvoicesTotalAmountAggregate_Sharded.aggregatedValue = "return record.totalIncVat"; + + hierarchy.allInvoicesByType = allInvoicesByType; + hierarchy.allInvoicesTotalAmountAggregate = allInvoicesTotalAmountAggregate; + hierarchy.allInvoicesPaidAmountAggregate = allInvoicesPaidAmountAggregate; + hierarchy.customersDefaultIndex = customersDefaultIndex; + hierarchy.allCustomersAgeFunctions = allCustomersAgeFunctions; + hierarchy.customersNoGroupaggregateGroup = customersNoGroupaggregateGroup; + hierarchy.invoicesByOutstandingIndex = invoicesByOutstandingIndex; + hierarchy.customersBySurnameIndex = customersBySurnameIndex; + hierarchy.outstandingInvoicesIndex = outstandingInvoicesIndex; + hierarchy.deceasedCustomersIndex = deceasedCustomersIndex; + hierarchy.customerInvoicesIndex = customerInvoicesIndex; +}; + +export const basicAppHierarchyCreator_WithFields = templateApi => + hierarchyFactory(withFields)(templateApi); + +export const basicAppHierarchyCreator_WithFields_AndIndexes = templateApi => + hierarchyFactory(withFields, withIndexes)(templateApi); + +export const setupApphierarchy = async (creator, disableCleanupTransactions=false) => { + const {templateApi} = getMemoryTemplateApi(); + const hierarchy = creator(templateApi); + await initialiseData(templateApi._storeHandle, {hierarchy:hierarchy.root, actions:[], triggers:[]}); + await templateApi.saveApplicationHierarchy(hierarchy.root); + const app = await appFromTempalteApi(templateApi, disableCleanupTransactions); + const collectionApi = getCollectionApi(app); + const indexApi = getIndexApi(app); + const authApi = getAuthApi(app); + const actionsApi = getActionsApi(app); + const recordApi = await getRecordApi(app); + recordApi._storeHandle = app.datastore; + actionsApi._app = app; + + const apis = { + recordApi, + collectionApi, + templateApi, + indexApi, + authApi, + actionsApi, + appHierarchy:hierarchy, + subscribe:templateApi._eventAggregator.subscribe, + app + }; + + return apis; +}; + +const disableCleanupTransactions = app => { + +} + +export const getNewFieldAndAdd = (templateApi, record) => (name, type, initial, typeOptions) => { + const field = templateApi.getNewField(type); + field.name = name; + field.getInitialValue = !initial ? "default" : initial; + if(!!typeOptions) + field.typeOptions = typeOptions; + templateApi.addField(record, field); + return field; +}; + +export const stubEventHandler = () => { + const events = []; + return { + handle: (name, context) => { + events.push({name, context}); + }, + events, + getEvents: n => filter(e => e.name === n) + (events) + }; +}; + +export const createValidActionsAndTriggers = () => { + const logMessage = createAction(); + logMessage.name = "logMessage"; + logMessage.behaviourName = "log"; + logMessage.behaviourSource = "budibase-behaviours"; + + const measureCallTime = createAction(); + measureCallTime.name = "measureCallTime"; + measureCallTime.behaviourName = "call_timer"; + measureCallTime.behaviourSource = "budibase-behaviours"; + + + const sendEmail = createAction(); + sendEmail.name = "sendEmail"; + sendEmail.behaviourName = "send_email"; + sendEmail.behaviourSource = "my-custom-lib"; + + const logOnErrorTrigger = createTrigger(); + logOnErrorTrigger.actionName = "logMessage"; + logOnErrorTrigger.eventName = "recordApi:save:onError"; + logOnErrorTrigger.optionsCreator = "return context.error.message;"; + + const timeCustomerSaveTrigger = createTrigger(); + timeCustomerSaveTrigger.actionName = "measureCallTime"; + timeCustomerSaveTrigger.eventName = "recordApi:save:onComplete"; + timeCustomerSaveTrigger.optionsCreator = "return 999;"; + timeCustomerSaveTrigger.condition = "context.record.type === 'customer'"; + + const allActions = [logMessage, measureCallTime, sendEmail]; + const allTriggers = [logOnErrorTrigger, timeCustomerSaveTrigger]; + + const behaviourSources = createBehaviourSources(); + const logs = []; + const call_timers = []; + const emails = []; + behaviourSources.register("budibase-behaviours", { + log: message => logs.push(message), + call_timer: opts => call_timers.push(opts) + }); + behaviourSources.register("my-custom-lib", { + send_email: em => emails.push(em) + }); + + return { + logMessage, measureCallTime, sendEmail, + logOnErrorTrigger, timeCustomerSaveTrigger, + allActions, allTriggers, behaviourSources, + logs, call_timers, emails + }; +}; + + +export const createAppDefinitionWithActionsAndTriggers = async () => { + + const {appHierarchy, templateApi, app, actionsApi} = await setupApphierarchy( + basicAppHierarchyCreator_WithFields + ); + + // adding validation rule so it can fail when we save it + templateApi.addRecordValidationRule(appHierarchy.customerRecord)( + templateApi.commonRecordValidationRules.fieldNotEmpty("surname") + ); + + await templateApi.saveApplicationHierarchy(appHierarchy.root); + + const actionsAndTriggers = createValidActionsAndTriggers(); + const {allActions, allTriggers, behaviourSources} = actionsAndTriggers; + await templateApi.saveActionsAndTriggers(allActions, allTriggers); + app.actions = initialiseActions( + templateApi._eventAggregator.subscribe, + behaviourSources, + allActions, + allTriggers); + app.user.permissions = generateFullPermissions(app); + app.behaviourSources = behaviourSources; + const appDefinition = await templateApi.getApplicationDefinition(); + return {templateApi, appDefinition, ...actionsAndTriggers, ...appHierarchy, app, actionsApi}; +}; + + +export const validUser = async (app, authApi, password, enabled=true, accessLevels=null) => { + const access = await authApi.getNewAccessLevel(app); + access.name = "admin"; + permission.setPassword.add(access); + + const access2 = await authApi.getNewAccessLevel(app); + access2.name = "admin2"; + permission.setPassword.add(access); + + await authApi.saveAccessLevels({version:0, levels:[access, access2]}); + + const u = authApi.getNewUser(app); + u.name = "bob"; + if(accessLevels === null) + u.accessLevels = ["admin"]; + else + u.accessLevels = accessLevels; + + u.enabled = enabled; + + await authApi.createUser(u, password); + return u; +}; \ No newline at end of file diff --git a/packages/core/test/support/jasmine.json b/packages/core/test/support/jasmine.json new file mode 100644 index 0000000000..89452f1277 --- /dev/null +++ b/packages/core/test/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "test", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/packages/core/test/templateApi.actionsValidation.spec.js b/packages/core/test/templateApi.actionsValidation.spec.js new file mode 100644 index 0000000000..07ddc79a25 --- /dev/null +++ b/packages/core/test/templateApi.actionsValidation.spec.js @@ -0,0 +1,120 @@ +import {validateActions, validateTrigger} from "../src/templateApi/validate"; +import {createValidActionsAndTriggers} from "./specHelpers"; + +describe("templateApi actions validation", () => { + + it("should return no errors when all actions are valid", () => { + + const {allActions} = createValidActionsAndTriggers(); + const result = validateActions(allActions); + expect(result).toEqual([]); + + }); + + it("should return error for empty behaviourName", () => { + + const {allActions, logMessage} = createValidActionsAndTriggers(); + logMessage.behaviourName = ""; + const result = validateActions(allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("behaviourName"); + + }); + + it("should return error for empty behaviourSource", () => { + + const {allActions, logMessage} = createValidActionsAndTriggers(); + logMessage.behaviourSource = ""; + const result = validateActions(allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("behaviourSource"); + + }); + + it("should return error for empty name", () => { + + const {allActions, logMessage} = createValidActionsAndTriggers(); + logMessage.name = ""; + const result = validateActions(allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("name"); + + }); + + it("should return error for duplicate name", () => { + + const {allActions, logMessage, measureCallTime} = createValidActionsAndTriggers(); + logMessage.name = measureCallTime.name; + const result = validateActions(allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual(""); + + }); + +}); + +describe("tempalteApi triggers validation", () => { + + it("should return error when actionName is empty", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.actionName = ""; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("actionName"); + }); + + it("should return error when eventName is empty", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.eventName = ""; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("eventName"); + }); + + it("should return error when eventName does not exist in allowed events", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.eventName = "non existant event name"; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("eventName"); + }); + + it("should return error when actionName does not exist in supplied actions", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.actionName = "non existent action name"; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("actionName"); + }); + + it("should return error when optionsCreator is invalid javascript", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.optionsCreator = "this is nonsense"; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("optionsCreator"); + }); + + it("should return error when condition is invalid javascript", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.condition = "this is nonsense"; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(1); + expect(result[0].field).toEqual("condition"); + }); + + it("should not return error when condition is empty", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.condition = ""; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(0); + }); + + it("should not return error when optionsCreator is empty", () => { + const {allActions, logOnErrorTrigger} = createValidActionsAndTriggers(); + logOnErrorTrigger.optionsCreator = ""; + const result = validateTrigger(logOnErrorTrigger, allActions); + expect(result.length).toBe(0); + }); +}); + diff --git a/packages/core/test/templateApi.constructHeirarchy.spec.js b/packages/core/test/templateApi.constructHeirarchy.spec.js new file mode 100644 index 0000000000..41f4f8f0be --- /dev/null +++ b/packages/core/test/templateApi.constructHeirarchy.spec.js @@ -0,0 +1,171 @@ +import {getMemoryTemplateApi} from "./specHelpers"; +import {errors} from "../src/templateApi"; + +describe("hierarchy node creation", () => { + + it("> getNewRootLevel > should be initialised with correct members", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + expect(root.name).toBe("root"); + expect(root.type).toBe("root"); + expect(root.children).toEqual([]); + expect(root.pathRegx).toBeDefined(); + expect(root.pathRegx()).toBe("/"); + expect(root.parent).toBeDefined(); + expect(root.isRoot()).toBeTruthy(); + expect(root.indexes).toEqual([]); + }); + + it("> getNewRecordTemplate > should be initialise with correct members", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root); + record.name = "child"; + expect(record.type).toBe("record"); + expect(record.children).toEqual([]); + expect(record.validationRules).toEqual([]); + expect(record.indexes).toEqual([]); + expect(record.parent()).toBe(root); + expect(record.collectionName).toBe(""); + expect(record.allidsShardFactor).toBe(64); + expect(record.isSingle).toBe(false); + + record.collectionName = "records"; + expect(record.collectionNodeKey()).toBe("/records"); + expect(record.collectionPathRegx()).toBe("/records"); + expect(record.nodeKey()).toBe(`/records/${record.nodeId}-{id}`); + expect(record.pathRegx()).toBe(`/records/${record.nodeId}-[a-zA-Z0-9_\-]+`); + }); + + it("> getNewSingleRecordTemplate > should set isSingle = true", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewSingleRecordTemplate(root); + expect(record.isSingle).toBe(true); + }); + + it("> getNewrecordTemplate > should have static pathRegx if is singlerecord", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewSingleRecordTemplate(root); + record.name = "child"; + expect(record.pathRegx()).toBe("/child"); + }); + + + it("> getNewrecordTemplate > should add itself to parent records's children", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const parentRecord = templateApi.getNewRecordTemplate(root); + const record = templateApi.getNewRecordTemplate(parentRecord); + expect(parentRecord.children.length).toBe(1); + expect(parentRecord.children[0]).toBe(record); + }); + + it("> getNewrecordTemplate > should add itself to parents's default index allowedNodeIds", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const parentRecord = templateApi.getNewRecordTemplate(root); + const record = templateApi.getNewRecordTemplate(parentRecord); + expect(root.indexes[0].allowedRecordNodeIds).toEqual([parentRecord.nodeId]); + expect(parentRecord.indexes[0].allowedRecordNodeIds).toEqual([record.nodeId]); + }); + + it("> getNewrecordTemplate > should add itself to root's children", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root); + expect(root.children.length).toBe(1); + expect(root.children[0]).toBe(record); + }); + + it("> getNewrecordTemplate > should have dynamic pathRegx if parent is record", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const parent = templateApi.getNewRecordTemplate(root); + parent.collectionName = "customers" + const record = templateApi.getNewRecordTemplate(parent); + record.name = "child"; + expect(record.pathRegx().startsWith("/customers")).toBe(true); + expect(record.pathRegx().includes("[")).toBe(true); + }); + + it("> getNewrecordTemplate > should add default index", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root, "rec"); + expect(root.indexes.length).toBe(1); + expect(root.indexes[0].name).toBe("rec_index"); + }); + + it("> getNewIndexTemplate > should initialise with correct members", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const index = templateApi.getNewIndexTemplate(root); + expect(index.type).toBe("index"); + expect(index.name).toBeDefined(); + expect(index.map).toBeDefined(); + expect(index.filter).toBeDefined(); + expect(index.children).toBeUndefined(); + expect(index.indexType).toBe("ancestor"); + expect(index.getShardName).toBeDefined(); + index.name = "naughty-customers"; + expect(index.pathRegx()).toBe("/naughty-customers"); + expect(index.parent()).toBe(root); + expect(index.aggregateGroups).toEqual([]); + }); + + it("> getNewIndexTemplate > should add itself to roots indexes", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const index = templateApi.getNewIndexTemplate(root); + expect(root.indexes.length).toBe(1); + expect(root.indexes[0]).toBe(index); + }); + + it("> getNewIndexTemplate > should add itself to record indexes", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root); + const index = templateApi.getNewIndexTemplate(record); + expect(record.indexes.length).toBe(1); + expect(record.indexes[0]).toBe(index); + expect(index.parent()).toBe(record); + }); + + it("should throw exception when no parent supplied, for non root node", async () => { + const {templateApi} = await getMemoryTemplateApi(); + expect(() => templateApi.getNewIndexTemplate()) + .toThrow(errors.allNonRootNodesMustHaveParent); + expect(() => templateApi.getNewRecordTemplate()) + .toThrow(errors.allNonRootNodesMustHaveParent); + }); + + it("> adding node > should just add one (bugfix)", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const parent = templateApi.getNewRecordTemplate(root); + templateApi.getNewRecordTemplate(parent); + + expect(root.children.length).toBe(1); + expect(parent.children.length).toBe(1); + }); + + it("> getNewAggregateGroupTemplate > should throw exception when non index supplied as parent", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + expect(() => templateApi.getNewAggregateGroupTemplate(root)) + .toThrow(); + }); + + it("> getNewAggregateGroupTemplate > should add itself to index aggregateGroups", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root); + const index = templateApi.getNewIndexTemplate(record); + const aggregateGroup = templateApi.getNewAggregateGroupTemplate(index); + expect(index.aggregateGroups.length).toBe(1); + expect(index.aggregateGroups[0]).toBe(aggregateGroup); + expect(aggregateGroup.parent()).toBe(index); + }); +}); \ No newline at end of file diff --git a/packages/core/test/templateApi.fields.spec.js b/packages/core/test/templateApi.fields.spec.js new file mode 100644 index 0000000000..7fe76c3213 --- /dev/null +++ b/packages/core/test/templateApi.fields.spec.js @@ -0,0 +1,117 @@ +import {isDefined, join, fieldDefinitions, $} from "../src/common"; +import {getMemoryTemplateApi} from "./specHelpers"; +import {fieldErrors} from "../src/templateApi/fields"; + +const getRecordTemplate = templateApi => + $(templateApi.getNewRootLevel(), [ + templateApi.getNewRecordTemplate + ]); + +const getValidField = templateApi => { + const field = templateApi.getNewField("string"); + field.name = "forename"; + field.label = "forename"; + return field; +}; + +const testMemberIsNotSet = membername => async () => { + const {templateApi} = await getMemoryTemplateApi(); + const field = getValidField(templateApi); + field[membername] = ""; + const errorsNotSet = templateApi.validateField([field])(field); + expect(errorsNotSet.length).toBe(1); + expect(errorsNotSet[0].error.includes("is not set")).toBeTruthy(); +}; + +const testMemberIsNotDefined = membername => async () => { + const {templateApi} = await getMemoryTemplateApi(); + const field = getValidField(templateApi); + delete field[membername]; + const errorsNotSet = templateApi.validateField([field])(field); + expect(errorsNotSet.length).toBe(1); + expect(errorsNotSet[0].error.includes("is not set")).toBeTruthy(); +}; + +describe("validateField", () => { + + it("should return error when name is not set", + testMemberIsNotSet("name")); + + it("should return error when name is not defined", + testMemberIsNotDefined("name")); + + it("should return error when type is not set", + testMemberIsNotSet("type")); + + it("should return error when type is not defined", + testMemberIsNotDefined("type")); + + it("should return error when label is not defined", + testMemberIsNotDefined("label")); + + it("should return error when getInitialValue is not defined", + testMemberIsNotDefined("getInitialValue")); + + it("should return error when getInitialValue is not set", + testMemberIsNotSet("getInitialValue")); + + it("should return error when getUndefinedValue is not defined", + testMemberIsNotDefined("getUndefinedValue")); + + it("should return error when getUndefinedValue is not set", + testMemberIsNotSet("getUndefinedValue")); + + it("should return no errors when valid field is supplied", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const field = getValidField(templateApi); + const errors = templateApi.validateField([field])(field); + expect(errors.length).toBe(0); + }); + + it("should return error when field with same name exists already", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const field1 = getValidField(templateApi); + field1.name = "surname"; + + const field2 = getValidField(templateApi); + field2.name = "surname"; + const errors = templateApi.validateField([field1, field2])(field2); + expect(errors.length).toBe(1); + expect(errors[0].error).toBe("field name is duplicated"); + expect(errors[0].field).toBe("name"); + }); + + it("should return error when field is not one of allowed types", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const field = getValidField(templateApi); + field.type = "sometype"; + const errors = templateApi.validateField([field])(field); + expect(errors.length).toBe(1); + expect(errors[0].error).toBe("type is unknown"); + expect(errors[0].field).toBe("type"); + }); + +}); + +describe("addField", () => { + + it("should throw exception when field is invalid", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const record = getRecordTemplate(templateApi); + const field = getValidField(templateApi); + field.name = ""; + expect(() => templateApi.addField(record, field)) + .toThrow(new RegExp('^' + fieldErrors.AddFieldValidationFailed, 'i')); + }); + + it("should add field when field is valid", async () => { + const {templateApi} = await getMemoryTemplateApi(); + const record = getRecordTemplate(templateApi); + const field = getValidField(templateApi); + field.name = "some_new_field"; + templateApi.addField(record, field); + expect(record.fields.length).toBe(1); + expect(record.fields[0]).toBe(field); + }); + +}); diff --git a/packages/core/test/templateApi.heirarchyValidation.spec.js b/packages/core/test/templateApi.heirarchyValidation.spec.js new file mode 100644 index 0000000000..1705d2a906 --- /dev/null +++ b/packages/core/test/templateApi.heirarchyValidation.spec.js @@ -0,0 +1,396 @@ +import {validateAll} from "../src/templateApi/validate"; +import createNodes from "../src/templateApi/createNodes"; +import {some} from "lodash"; +import {getNewField, addField} from "../src/templateApi/fields"; +import {getNewRecordValidationRule, commonRecordValidationRules, + addRecordValidationRule} from "../src/templateApi/recordValidationRules"; +import { findField } from "../src/templateApi/hierarchy"; +import {findCollectionDefaultIndex} from "./specHelpers"; + +const createValidHierarchy = () => { + const root = createNodes.getNewRootLevel(); + + const customerRecord = createNodes.getNewRecordTemplate(root, "customer"); + customerRecord.collectionName = "customers"; + + const customersDefaultIndex = findCollectionDefaultIndex(customerRecord); + const customersNoGroupaggregateGroup = createNodes.getNewAggregateGroupTemplate(customersDefaultIndex); + customersNoGroupaggregateGroup.name = "Customers Summary"; + const allCustomersOwedFunctions = createNodes.getNewAggregateTemplate(customersNoGroupaggregateGroup); + allCustomersOwedFunctions.aggregatedValue = "return record.owed"; + allCustomersOwedFunctions.name = "all customers owed amount"; + + const partnerRecord = createNodes.getNewRecordTemplate(root, "partner"); + partnerRecord.collectionName = "partners"; + partnerRecord.name = "partner"; + const businessName = getNewField("string"); + businessName.name = "businessname"; + businessName.label = "bn"; + addField(partnerRecord,businessName); + + + customerRecord.name = "customer"; + const surnameField = getNewField("string"); + surnameField.name = "surname"; + surnameField.label = "surname"; + const isaliveField = getNewField("bool"); + isaliveField.name = "isalive"; + const createddateField = getNewField("datetime"); + createddateField.name = "createddate"; + const ageField = getNewField("number"); + ageField.name = "age"; + const partnerField = getNewField("reference"); + partnerField.name = "partner"; + partnerField.typeOptions.indexNodeKey = "l"; + partnerField.typeOptions.reverseIndexNodeKeys = ["l"]; + partnerField.typeOptions.displayValue = "l"; + const otherNamesField = getNewField("array"); + otherNamesField.name = "othernames"; + addField(customerRecord, surnameField); + addField(customerRecord, isaliveField); + addField(customerRecord, createddateField); + addField(customerRecord, ageField); + addField(customerRecord, partnerField); + addField(customerRecord, otherNamesField); + addRecordValidationRule(customerRecord) + (commonRecordValidationRules.fieldNotEmpty("surname")); + + return { + root, + customerRecord, customersDefaultIndex, + customersNoGroupaggregateGroup, + allCustomersOwedFunctions + } +}; + +describe("hierarchy validation", () => { + + const expectInvalidField = (validationResult, fieldName, expectedNode, count = 1) => { + expect(validationResult.length).toBe(count); + expect(some(validationResult, r => r.field === fieldName && r.item === expectedNode)).toBe(true); + } + + it("should return no errors when hierarchy is valid", () => { + const hierarchy = createValidHierarchy(); + const validationResult = validateAll(hierarchy.root); + expect(validationResult).toEqual([]); + }); + + it("should return an error on name field, when name not set, on all nodes types", () => { + let hierarchy = createValidHierarchy(); + const expectInvalidName = (node) => expectInvalidField(validationResult, "name", node, 1); + + hierarchy = createValidHierarchy(); + hierarchy.customerRecord.name = ""; + let validationResult = validateAll(hierarchy.root); + expectInvalidName(hierarchy.customerRecord); + hierarchy.customerRecord.name = "customers"; + + hierarchy = createValidHierarchy(); + hierarchy.customerRecord.name = ""; + validationResult = validateAll(hierarchy.root); + expectInvalidName(hierarchy.customerRecord); + hierarchy.customerRecord.name = "customer"; + + }); + + it("record > should return an error on fields member if empty", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customerRecord.fields = []; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "fields", hierarchy.customerRecord); + }); + + it("record > should return an error on unrecognised type", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customerRecord.type = "notatype"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "type", hierarchy.customerRecord); + }); + + it("record > should return an error when validation rules do not have correct members", () => { + let hierarchy = createValidHierarchy(); + delete hierarchy.customerRecord.validationRules[0].expressionWhenValid; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "validationRules", hierarchy.customerRecord); + + hierarchy = createValidHierarchy(); + delete hierarchy.customerRecord.validationRules[0].messageWhenInvalid; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "validationRules", hierarchy.customerRecord); + + }); + + it("collection > should return error when duplicate names", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customerRecord.collectionName = "partners" + hierarchy.customerRecord.name = "partner" + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "name", hierarchy.customerRecord, 2); + }); + + it("index > should return error when index has no map", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersDefaultIndex.map = ""; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "map", hierarchy.customersDefaultIndex); + }); + + it("index > should return error when index map function does not compile", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersDefaultIndex.map = "invalid js!!"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "map", hierarchy.customersDefaultIndex); + }); + + it("index > should return error when index filter function does not compile", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersDefaultIndex.filter = "invalid js!!"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "filter", hierarchy.customersDefaultIndex); + }); + + it("index > should return error when index type is not one of allowed values", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersDefaultIndex.indexType = ""; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "indexType", hierarchy.customersDefaultIndex); + + hierarchy.customersDefaultIndex.indexType = "should not be allowed"; + const validationResult2 = validateAll(hierarchy.root); + expectInvalidField(validationResult2, "indexType", hierarchy.customersDefaultIndex); + }); + + it("index > should return error when reference index's parent is not a record", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersDefaultIndex.indexType = "reference"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "indexType", hierarchy.customersDefaultIndex); + }); + + it("field > should return error when a field is invalid", () => { + const hierarchy = createValidHierarchy(); + const invalidField =hierarchy.customerRecord.fields[0]; + invalidField.name = ""; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "name", invalidField); + }); + + it("aggregateGroup > should return error when name is not supplied", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersNoGroupaggregateGroup.name = ""; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "name", hierarchy.customersNoGroupaggregateGroup); + }); + + it("aggregate > should return error when name note set", () => { + const hierarchy = createValidHierarchy(); + hierarchy.allCustomersOwedFunctions.name = ""; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "name", hierarchy.allCustomersOwedFunctions); + }); + + it("aggregate > should return error when condition does not compile", () => { + const hierarchy = createValidHierarchy(); + hierarchy.customersNoGroupaggregateGroup.condition = "invalid condition"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "condition", hierarchy.customersNoGroupaggregateGroup); + }); + + it("aggregate > should return error when aggregatedValue does not compile", () => { + const hierarchy = createValidHierarchy(); + hierarchy.allCustomersOwedFunctions.aggregatedValue = "invalid value"; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "aggregatedValue", hierarchy.allCustomersOwedFunctions); + }); + + it("aggregate > should be valid when valid condition and aggregatedValue supplied", () => { + const hierarchy = createValidHierarchy(); + hierarchy.allCustomersOwedFunctions.aggregatedValue = "return record.owed;"; + hierarchy.allCustomersOwedFunctions.condition = "record.owed > 0;"; + const validationResult = validateAll(hierarchy.root); + expect(validationResult.length).toBe(0); + }); + + it("field.typeOptions > string > should return error when maxLength <= 0", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "surname"); + invalidField.typeOptions.maxLength = -1; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxLength", invalidField); + + invalidField.typeOptions.maxLength = 0; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxLength", invalidField); + + invalidField.typeOptions.maxLength = 1; + validationResult = validateAll(hierarchy.root); + validationResult.length === 0; + }); + + it("field.typeOptions > string > should return error allowDeclaredValues only is not a bool", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "surname"); + invalidField.typeOptions.allowDeclaredValuesOnly = null; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.allowDeclaredValuesOnly", invalidField); + + invalidField.typeOptions.allowDeclaredValuesOnly = ""; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.allowDeclaredValuesOnly", invalidField); + + }); + + it("field.typeOptions > string > should return error when values contains non-string", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "surname"); + invalidField.typeOptions.values = [1]; + const validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.values", invalidField); + }); + + it("field.typeOptions > bool > should return error when allowNulls is not a bool", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "isalive"); + invalidField.typeOptions.allowNulls = "1"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.allowNulls", invalidField); + + invalidField.typeOptions.allowNulls = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.allowNulls", invalidField); + }); + + it("field.typeOptions > datetime > should return error when maxValue is not a date", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "createddate"); + invalidField.typeOptions.maxValue = "1"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxValue", invalidField); + + invalidField.typeOptions.maxValue = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxValue", invalidField); + }); + + it("field.typeOptions > datetime > should return error when minValue is not a date", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "createddate"); + invalidField.typeOptions.minValue = "1"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.minValue", invalidField); + + invalidField.typeOptions.minValue = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.minValue", invalidField); + }); + + it("field.typeOptions > number > should return error when minValue is not an integer", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "age"); + invalidField.typeOptions.minValue = "hello"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.minValue", invalidField); + + invalidField.typeOptions.minValue = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.minValue", invalidField); + + invalidField.typeOptions.minValue = 1.1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.minValue", invalidField); + }); + + it("field.typeOptions > number > should return error when maxValue is not an integer", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "age"); + invalidField.typeOptions.maxValue = "hello"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxValue", invalidField); + + invalidField.typeOptions.maxValue = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxValue", invalidField); + + invalidField.typeOptions.maxValue = 1.1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.maxValue", invalidField); + }); + + it("field.typeOptions > number > should return error when decimal places is not a positive integer", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "age"); + invalidField.typeOptions.decimalPlaces = "hello"; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.decimalPlaces", invalidField); + + invalidField.typeOptions.decimalPlaces = null; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.decimalPlaces", invalidField); + + invalidField.typeOptions.decimalPlaces = -1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.decimalPlaces", invalidField); + + invalidField.typeOptions.decimalPlaces = 1.1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.decimalPlaces", invalidField); + }); + + it("field.typeOptions > reference > should return error when indexNodeKey is not a compmleted string", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "partner"); + invalidField.typeOptions.indexNodeKey = null; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.indexNodeKey", invalidField); + + invalidField.typeOptions.indexNodeKey = ""; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.indexNodeKey", invalidField); + + invalidField.typeOptions.indexNodeKey = 1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.indexNodeKey", invalidField); + }); + + it("field.typeOptions > reference > should return error when reverseIndexNodeKeys is not a string array of >0 length", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "partner"); + invalidField.typeOptions.reverseIndexNodeKeys = null; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.reverseIndexNodeKeys", invalidField); + + invalidField.typeOptions.reverseIndexNodeKeys = ""; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.reverseIndexNodeKeys", invalidField); + + invalidField.typeOptions.reverseIndexNodeKeys = []; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.reverseIndexNodeKeys", invalidField); + + invalidField.typeOptions.reverseIndexNodeKeys = "/not/an/array"; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.reverseIndexNodeKeys", invalidField); + + invalidField.typeOptions.reverseIndexNodeKeys = ["/some/key/here"]; + validationResult = validateAll(hierarchy.root); + expect(validationResult.length).toBe(0); + }); + + it("field.typeOptions > reference > should return error when displayValue is not a compmleted string", () => { + const hierarchy = createValidHierarchy(); + const invalidField = findField(hierarchy.customerRecord, "partner"); + invalidField.typeOptions.displayValue = null; + let validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.displayValue", invalidField); + + invalidField.typeOptions.displayValue = ""; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.displayValue", invalidField); + + invalidField.typeOptions.displayValue = 1; + validationResult = validateAll(hierarchy.root); + expectInvalidField(validationResult, "typeOptions.displayValue", invalidField); + }); + +}); diff --git a/packages/core/test/templateApi.loadSaveHeirarchy.spec.js b/packages/core/test/templateApi.loadSaveHeirarchy.spec.js new file mode 100644 index 0000000000..61972137c6 --- /dev/null +++ b/packages/core/test/templateApi.loadSaveHeirarchy.spec.js @@ -0,0 +1,176 @@ +import {getMemoryTemplateApi} from "./specHelpers"; +import {permission} from "../src/authApi/permissions"; +const saveThreeLevelHierarchy = async () => { + const {templateApi, app} = await getMemoryTemplateApi(); + const root = templateApi.getNewRootLevel(); + const record = templateApi.getNewRecordTemplate(root); + record.name = "customer"; + const surname = templateApi.getNewField("string"); + surname.name = "surname"; + surname.label = "surname"; + templateApi.addField(record, surname); + + await templateApi.saveApplicationHierarchy(root); + return {templateApi, root, record, app}; +}; + +describe("Load & Save App Hierarchy", () => { + + it("should rehydrate json objects with pathRegx methods", async () => { + + const {templateApi} = await saveThreeLevelHierarchy(); + const {hierarchy} = await templateApi.getApplicationDefinition(); + + expect(hierarchy.pathRegx).toBeDefined(); + expect(hierarchy.children[0].pathRegx).toBeDefined(); + + }); + + it("should rehydrate json objects with parent methods", async () => { + + const {templateApi} = await saveThreeLevelHierarchy(); + const {hierarchy} = await templateApi.getApplicationDefinition(); + + expect(hierarchy.parent).toBeDefined(); + expect(hierarchy.children[0].parent).toBeDefined(); + + }); + + + it("should throw error when validation fails", async () => { + + const {templateApi, record, root} = await saveThreeLevelHierarchy(); + record.name = ""; + + let err; + try { + await templateApi.saveApplicationHierarchy(root); + } catch(e) { + err = e; + } + expect(err).toBeDefined(); + + }); + + it("should load hierarchy with exactly the same members - balls deep", async () => { + + const {templateApi, root} = await saveThreeLevelHierarchy(); + const {hierarchy} = await templateApi.getApplicationDefinition(); + + expect(JSON.stringify(hierarchy)) + .toEqual(JSON.stringify(root)); + }); + + it("should throw an error when app definition does not exist", async () => { + let ex; + try{ + await templateApi.getApplicationDefinition() + } + catch(e) { + ex = e; + } + expect(ex).toBeDefined(); + }); + + it("should create .config folder on first save ", async () => { + const {templateApi} = await saveThreeLevelHierarchy(); + expect(await templateApi._storeHandle.exists("/.config")).toBeTruthy(); + }); + + it("should throw error when user user does not have permission", async () => { + const {templateApi, app, root} = await saveThreeLevelHierarchy(); + app.removePermission(permission.writeTemplates.get()); + expect(templateApi.saveApplicationHierarchy(root)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {templateApi, app, root} = await saveThreeLevelHierarchy(); + app.withOnlyThisPermission(permission.writeTemplates.get()); + await templateApi.saveApplicationHierarchy(root); + }); + +}); + +describe("save load actions", () => { + + const appDefinitionWithTriggersAndActions = async () => { + const {templateApi, app} = await saveThreeLevelHierarchy(); + + const logAction = templateApi.createAction(); + logAction.behaviourName = "log"; + logAction.behaviourSource = "test"; + logAction.name = "log_something"; + + const logOnErrorTrigger = templateApi.createTrigger(); + logOnErrorTrigger.actionName = "log_something"; + logOnErrorTrigger.eventName = "recordApi:save:onError"; + + return ({ + templateApi, + actions:[logAction], + triggers:[logOnErrorTrigger], + app + }); + + } + + it("should load actions with exactly the same members", async () => { + + const {templateApi, actions, triggers} = + await appDefinitionWithTriggersAndActions(); + + await templateApi.saveActionsAndTriggers(actions, triggers); + + const appDef = await templateApi.getApplicationDefinition(); + + expect(appDef.actions).toEqual(actions); + expect(appDef.triggers).toEqual(triggers); + }); + + it("should throw error when actions are invalid", async () => { + const {templateApi, actions, triggers} = + await appDefinitionWithTriggersAndActions(); + + actions[0].name = ""; + + let err; + try { + await templateApi.saveActionsAndTriggers(actions, triggers); + } catch (e) { + err = e; + } + + expect(err).toBeDefined(); + }); + + it("should throw error when triggers are invalid", async () => { + const {templateApi, actions, triggers} = + await appDefinitionWithTriggersAndActions(); + + triggers[0].eventName = ""; + + let err; + try { + await templateApi.saveActionsAndTriggers(actions, triggers); + } catch (e) { + err = e; + } + + expect(err).toBeDefined(); + }); + + it("should throw error when user user does not have permission", async () => { + const {templateApi, actions, triggers, app} = + await appDefinitionWithTriggersAndActions(); + app.removePermission(permission.writeTemplates.get()); + expect(templateApi.saveActionsAndTriggers(actions, triggers)).rejects.toThrow(/Unauthorized/); + }); + + it("should not depend on having any other permissions", async () => { + const {templateApi, actions, triggers, app} = + await appDefinitionWithTriggersAndActions(); + app.withOnlyThisPermission(permission.writeTemplates.get()); + await templateApi.saveActionsAndTriggers(actions, triggers); + }); + +}); \ No newline at end of file diff --git a/packages/core/test/templateApi.types.spec.js b/packages/core/test/templateApi.types.spec.js new file mode 100644 index 0000000000..2746c8553b --- /dev/null +++ b/packages/core/test/templateApi.types.spec.js @@ -0,0 +1,229 @@ +import {getNewFieldValue, safeParseField} from "../src/types"; +import {getNewField} from "../src/templateApi/fields"; +import { isDefined } from "../src/common"; + +const getField = type => { + const field = getNewField(type); + return field; +}; + +const nothingReference = {key:""}; +const nothingFile = {relativePath:"",size:0}; + +describe("types > getNew", () => { + + const defaultAlwaysNull = type => () => { + const field = getField(type); + field.getInitialValue = "default"; + const value = getNewFieldValue(field); + expect(value).toBe(null); + } + + it("bool should return null when fields getInitialValue is 'default'", + defaultAlwaysNull("bool")); + + it("string should return null when fields getInitialValue is 'default'", + defaultAlwaysNull("string")); + + it("number should return null when fields getInitialValue is 'default'", + defaultAlwaysNull("number")); + + it("datetime should return null when fields getInitialValue is 'default'", + defaultAlwaysNull("datetime")); + + it("reference should return {key:''} when fields getInitialValue is 'default'", () => { + const field = getField("reference"); + field.getInitialValue = "default"; + const value = getNewFieldValue(field); + expect(value).toEqual(nothingReference); + }); + + it("file should return {relativePath:'', size:0} when fields getInitialValue is 'default'", () => { + const field = getField("file"); + field.getInitialValue = "default"; + const value = getNewFieldValue(field); + expect(value).toEqual(nothingFile); + }); + + it("array should return empty array when field getInitialValue is 'default'", () => { + const field = getField("array"); + field.getInitialValue = "default"; + const value = getNewFieldValue(field); + expect(value).toEqual([]); + }); + + it("datetime should return Now when getInitialValue is 'now'", () => { + const field = getField("datetime"); + field.getInitialValue = "now"; + const before = new Date(); + const value = getNewFieldValue(field); + const after = new Date(); + expect(value >= before && value <= after).toBeTruthy(); + }); + + const test_getNewFieldValue = (type, val, expected) => () => { + const field = getField(type); + field.getInitialValue = val; + const value = getNewFieldValue(field); + expect(value).toEqual(expected); + }; + + it("bool should parse value in getInitialValue if function not recognised", () => { + test_getNewFieldValue("bool", "true", true)(); + test_getNewFieldValue("bool", "on", true)(); + test_getNewFieldValue("bool", "1", true)(); + test_getNewFieldValue("bool", "yes", true)(); + test_getNewFieldValue("bool", "false", false)(); + test_getNewFieldValue("bool", "off", false)(); + test_getNewFieldValue("bool", "0", false)(); + test_getNewFieldValue("bool", "no", false)(); + }); + + it("bool should return null if function not recognised and value cannot be parsed", () => { + test_getNewFieldValue("bool", "blah", null)(); + test_getNewFieldValue("bool", 111, null)(); + }); + + it("number should parse value in getInitialValue if function not recognised", () => { + test_getNewFieldValue("number", "1", 1)(); + test_getNewFieldValue("number", "45", 45)(); + test_getNewFieldValue("number", "4.11", 4.11)(); + }); + + it("number should return null if function not recognised and value cannot be parsed", () => { + test_getNewFieldValue("number", "blah", null)(); + test_getNewFieldValue("number", true, null)(); + }); + + it("string should parse value in getInitialValue if function not recognised", () => { + test_getNewFieldValue("string", "hello there", "hello there")(); + test_getNewFieldValue("string", 45, "45")(); + test_getNewFieldValue("string", true, "true")(); + }); + + it("array should return empty array when function not recognised", () => { + test_getNewFieldValue("array", "blah", [])(); + test_getNewFieldValue("array", true, [])(); + test_getNewFieldValue("array", 1, [])(); + test_getNewFieldValue("array", "", [])(); + test_getNewFieldValue("array", "", [])(); + test_getNewFieldValue("array", "", [])(); + }); + + it("reference should {key:''} when function not recognised", () => { + test_getNewFieldValue("reference", "blah", nothingReference)(); + }); + + it("file should return {relativePath:'',size:0} when function not recognised", () => { + test_getNewFieldValue("file", "blah", nothingFile)(); + }); +}); + +describe("types > getSafeFieldValue", () => { + + const test_getSafeFieldValue = (type, member, value, expectedParse) => () => { + const field = getField(type); + field.getDefaultValue = "default"; + field.name = member; + const record = {}; + if(isDefined(value)) + record[member] = value; + const parsedvalue = safeParseField(field, record); + expect(parsedvalue).toEqual(expectedParse); + }; + + it("should get default field value when member is undefined on record", + test_getSafeFieldValue("string", "forename", undefined, null)); + + it("should return null as null (except array and reference)", () => { + test_getSafeFieldValue("string", "forename", null, null)(); + test_getSafeFieldValue("bool", "isalive", null, null)(); + test_getSafeFieldValue("datetime", "created", null, null)(); + test_getSafeFieldValue("number", "age", null, null)(); + test_getSafeFieldValue("array", "tags", null, [])(); + test_getSafeFieldValue("reference", "moretags", null, nothingReference)(); + test_getSafeFieldValue("file", "moretags", null, nothingFile)(); + }); + + it("bool should parse a defined set of true/false aliases", () => { + test_getSafeFieldValue("bool", "isalive", true, true)(); + test_getSafeFieldValue("bool", "isalive", "true", true)(); + test_getSafeFieldValue("bool", "isalive", "on", true)(); + test_getSafeFieldValue("bool", "isalive", "1", true)(); + test_getSafeFieldValue("bool", "isalive", "yes", true)(); + test_getSafeFieldValue("bool", "isalive", false, false)(); + test_getSafeFieldValue("bool", "isalive", "false", false)(); + test_getSafeFieldValue("bool", "isalive", "off", false)(); + test_getSafeFieldValue("bool", "isalive", "0", false)(); + test_getSafeFieldValue("bool", "isalive", "no", false)(); + }); + + it("bool should parse invalid values as null", + test_getSafeFieldValue("bool", "isalive", "blah", null)); + + it("number should parse numbers and strings that are numbers", () => { + test_getSafeFieldValue("number", "age", 204, 204)(); + test_getSafeFieldValue("number", "age","1", 1)(); + test_getSafeFieldValue("number", "age","45", 45)(); + test_getSafeFieldValue("number", "age","4.11", 4.11)(); + }); + + it("number should parse invalid values as null", + test_getSafeFieldValue("number", "age","blah", null)); + + it("string should parse strings", + test_getSafeFieldValue("string", "forename", "bob", "bob")); + + it("string should parse any other basic type", () => { + test_getSafeFieldValue("string", "forename", true, "true")(); + test_getSafeFieldValue("string", "forename", 1, "1")(); + }); + + it("date should parse dates in various precisions", () => { + // dont forget that JS Date's month is zero based + test_getSafeFieldValue("datetime", "createddate", "2018-02-14", new Date(2018, 1, 14))(); + test_getSafeFieldValue("datetime", "createddate", "2018-2-14", new Date(2018, 1, 14))(); + test_getSafeFieldValue("datetime", "createddate", "2018-02-14 11:00:00.000", new Date(2018, 1, 14, 11))(); + test_getSafeFieldValue("datetime", "createddate", "2018-02-14 11:30", new Date(2018, 1, 14, 11, 30))(); + }); + + it("date should parse invalid dates as null", () => { + // dont forget that JS Date's month is zero based + test_getSafeFieldValue("datetime", "createddate", "2018-13-14", null)(); + test_getSafeFieldValue("datetime", "createddate", "2018-2-33", null)(); + test_getSafeFieldValue("datetime", "createddate", "bla", null)(); + }); + + it("array should parse array", () => { + test_getSafeFieldValue("array", "tags", ["bob","the","dog"], ["bob","the","dog"])(); + test_getSafeFieldValue("array", "tags", [true, false], [true, false])(); + test_getSafeFieldValue("array", "tags", [1,2,3,4], [1,2,3,4])(); + test_getSafeFieldValue("array", "tags", + [{key:"/customer/1234", value:"bob"}], + [{key:"/customer/1234", value:"bob"}])(); + }); + + it("array should convert the generic's child type", () => { + test_getSafeFieldValue("array", "tags", [1,true], ["1","true"])(); + test_getSafeFieldValue( + "array", + "tags", + ["yes","true","no", "false", true, false], + [true, true, false, false, true, false])(); + test_getSafeFieldValue("array", "tags", ["1",23], [1,23])(); + + }); + + it("reference should parse reference", () => { + test_getSafeFieldValue("reference", "customer", + {key:"/customer/1234", value:"bob"}, + {key:"/customer/1234", value:"bob"})(); + }); + + it("reference should parse reference", () => { + test_getSafeFieldValue("file", "profilepic", + {relativePath:"path/to/pic.jpg", size:120}, + {relativePath:"path/to/pic.jpg", size:120})(); + }); + +}); \ No newline at end of file diff --git a/packages/core/yarn.lock b/packages/core/yarn.lock new file mode 100644 index 0000000000..a7d2bae8f1 --- /dev/null +++ b/packages/core/yarn.lock @@ -0,0 +1,6357 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/cli@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.4.4.tgz#5454bb7112f29026a4069d8e6f0e1794e651966c" + integrity sha512-XGr5YjQSjgTa6OzQZY57FAJsdeVSAKR/u/KA5exWIz66IKtv/zXtHy+fIZcMry/EgYegwuHE7vzGnrFhjdIAsQ== + dependencies: + commander "^2.8.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.1.0" + glob "^7.0.0" + lodash "^4.17.11" + mkdirp "^0.5.1" + output-file-sync "^2.0.0" + slash "^2.0.0" + source-map "^0.5.0" + optionalDependencies: + chokidar "^2.0.4" + +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/core@^7.1.0", "@babel/core@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a" + integrity sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.4.4" + "@babel/helpers" "^7.4.4" + "@babel/parser" "^7.4.5" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.4.5" + "@babel/types" "^7.4.4" + convert-source-map "^1.1.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.11" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041" + integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ== + dependencies: + "@babel/types" "^7.4.4" + jsesc "^2.5.1" + lodash "^4.17.11" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/generator@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.0.tgz#f20e4b7a91750ee8b63656073d843d2a736dca4a" + integrity sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA== + dependencies: + "@babel/types" "^7.5.0" + jsesc "^2.5.1" + lodash "^4.17.11" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" + integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" + integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-call-delegate@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" + integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/helper-define-map@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a" + integrity sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/types" "^7.4.4" + lodash "^4.17.11" + +"@babel/helper-explode-assignable-expression@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" + integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== + dependencies: + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" + integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== + dependencies: + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" + integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-hoist-variables@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a" + integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-member-expression-to-functions@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz#8cd14b0a0df7ff00f009e7d7a436945f47c7a16f" + integrity sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-imports@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" + integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8" + integrity sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/types" "^7.4.4" + lodash "^4.17.11" + +"@babel/helper-optimise-call-expression@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" + integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2" + integrity sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q== + dependencies: + lodash "^4.17.11" + +"@babel/helper-remap-async-to-generator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" + integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-wrap-function" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27" + integrity sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/helper-simple-access@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" + integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w== + dependencies: + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-wrap-function@^7.1.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" + integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.2.0" + +"@babel/helpers@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5" + integrity sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A== + dependencies: + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.0.0", "@babel/parser@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.0.tgz#3e0713dff89ad6ae37faec3b29dcfc5c979770b7" + integrity sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA== + +"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872" + integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" + integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + +"@babel/plugin-proposal-object-rest-spread@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005" + integrity sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" + integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" + integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" + integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-async-to-generator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894" + integrity sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" + integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-block-scoping@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d" + integrity sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + lodash "^4.17.11" + +"@babel/plugin-transform-classes@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6" + integrity sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-define-map" "^7.4.4" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.4.4" + "@babel/helper-split-export-declaration" "^7.4.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" + integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f" + integrity sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" + integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3" + integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-for-of@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" + integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-function-name@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" + integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" + integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-member-expression-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d" + integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6" + integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-commonjs@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e" + integrity sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw== + dependencies: + "@babel/helper-module-transforms" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + +"@babel/plugin-transform-modules-systemjs@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405" + integrity sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" + integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" + integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg== + dependencies: + regexp-tree "^0.1.6" + +"@babel/plugin-transform-new-target@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" + integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-object-super@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" + integrity sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.1.0" + +"@babel/plugin-transform-parameters@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" + integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== + dependencies: + "@babel/helper-call-delegate" "^7.4.4" + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-property-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905" + integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-regenerator@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f" + integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-reserved-words@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634" + integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-runtime@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz#a50f5d16e9c3a4ac18a1a9f9803c107c380bce08" + integrity sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" + integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-spread@^7.2.0": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" + integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" + integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + +"@babel/plugin-transform-template-literals@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" + integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" + integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-unicode-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" + integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/preset-env@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58" + integrity sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.4.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.4.4" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.4.4" + "@babel/plugin-transform-classes" "^7.4.4" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.4.4" + "@babel/plugin-transform-function-name" "^7.4.4" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-member-expression-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.4.4" + "@babel/plugin-transform-modules-systemjs" "^7.4.4" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5" + "@babel/plugin-transform-new-target" "^7.4.4" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.4.4" + "@babel/plugin-transform-property-literals" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.4.5" + "@babel/plugin-transform-reserved-words" "^7.2.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.4.4" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.4.4" + "@babel/types" "^7.4.4" + browserslist "^4.6.0" + core-js-compat "^3.1.1" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.5.0" + +"@babel/runtime@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" + integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/traverse@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.0.tgz#4216d6586854ef5c3c4592dab56ec7eb78485485" + integrity sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.5.0" + "@babel/types" "^7.5.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.11" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216" + integrity sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.4.4" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.4.5" + "@babel/types" "^7.4.4" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.11" + +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0" + integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ== + dependencies: + esutils "^2.0.2" + lodash "^4.17.11" + to-fast-properties "^2.0.0" + +"@babel/types@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.0.tgz#e47d43840c2e7f9105bc4d3a2c371b4d0c7832ab" + integrity sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ== + dependencies: + esutils "^2.0.2" + lodash "^4.17.11" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" + integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@jest/console@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.7.1.tgz#32a9e42535a97aedfe037e725bd67e954b459545" + integrity sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg== + dependencies: + "@jest/source-map" "^24.3.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.8.0.tgz#fbbdcd42a41d0d39cddbc9f520c8bab0c33eed5b" + integrity sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.8.0" + jest-config "^24.8.0" + jest-haste-map "^24.8.0" + jest-message-util "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve-dependencies "^24.8.0" + jest-runner "^24.8.0" + jest-runtime "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + jest-watcher "^24.8.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + pirates "^4.0.1" + realpath-native "^1.1.0" + rimraf "^2.5.4" + strip-ansi "^5.0.0" + +"@jest/environment@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.8.0.tgz#0342261383c776bdd652168f68065ef144af0eac" + integrity sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw== + dependencies: + "@jest/fake-timers" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + +"@jest/fake-timers@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.8.0.tgz#2e5b80a4f78f284bcb4bd5714b8e10dd36a8d3d1" + integrity sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw== + dependencies: + "@jest/types" "^24.8.0" + jest-message-util "^24.8.0" + jest-mock "^24.8.0" + +"@jest/reporters@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.8.0.tgz#075169cd029bddec54b8f2c0fc489fd0b9e05729" + integrity sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.1.1" + jest-haste-map "^24.8.0" + jest-resolve "^24.8.0" + jest-runtime "^24.8.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + node-notifier "^5.2.1" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + +"@jest/source-map@^24.3.0": + version "24.3.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.3.0.tgz#563be3aa4d224caf65ff77edc95cd1ca4da67f28" + integrity sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.8.0.tgz#7675d0aaf9d2484caa65e048d9b467d160f8e9d3" + integrity sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng== + dependencies: + "@jest/console" "^24.7.1" + "@jest/types" "^24.8.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz#2f993bcf6ef5eb4e65e8233a95a3320248cf994b" + integrity sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg== + dependencies: + "@jest/test-result" "^24.8.0" + jest-haste-map "^24.8.0" + jest-runner "^24.8.0" + jest-runtime "^24.8.0" + +"@jest/transform@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.8.0.tgz#628fb99dce4f9d254c6fd9341e3eea262e06fef5" + integrity sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.8.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.8.0" + jest-regex-util "^24.3.0" + jest-util "^24.8.0" + micromatch "^3.1.10" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + +"@jest/types@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad" + integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^12.0.9" + +"@nx-js/compiler-util@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297" + integrity sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ== + +"@phc/format@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@phc/format/-/format-0.5.0.tgz#a99d27a83d78b3100a191412adda04315e2e3aba" + integrity sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg== + dependencies: + safe-buffer "^5.1.2" + +"@types/babel__core@^7.1.0": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" + integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" + integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.6.tgz#328dd1a8fc4cfe3c8458be9477b219ea158fd7b2" + integrity sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" + integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + +"@types/node@*", "@types/node@^12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40" + integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA== + +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/yargs@^12.0.2", "@types/yargs@^12.0.9": + version "12.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916" + integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw== + +abab@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" + integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abstract-leveldown@~0.12.0, abstract-leveldown@~0.12.1: + version "0.12.4" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz#29e18e632e60e4e221d5810247852a63d7b2e410" + integrity sha1-KeGOYy5g5OIh1YECR4UqY9ey5BA= + dependencies: + xtend "~3.0.0" + +acorn-globals@^4.1.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.2.tgz#4e2c2313a597fd589720395f6354b41cd5ec8006" + integrity sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" + integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== + +acorn-walk@^6.0.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" + integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== + +acorn@^5.5.3, acorn@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + +acorn@^6.0.1, acorn@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== + +acorn@^6.0.7: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.0.tgz#67f0da2fc339d6cfb5d6fb244fd449f33cd8bbe3" + integrity sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw== + +ajv@^6.5.5: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.9.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.1.tgz#ebf8d3af22552df9dd049bfbe50cc2390e823593" + integrity sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0, ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argon2@^0.20.1: + version "0.20.1" + resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.20.1.tgz#f58d2a0aeaf88bbe5762df02c3455e6f4f1bbbf3" + integrity sha512-ds6SU6YAXoJQGgc9tMOfb55Dyls+b3oaY9bSED0/O83aqlBOEEKR+mbmrR37MmlGaDqKrGDfWoTlHUqeZw8sHQ== + dependencies: + "@phc/format" "^0.5.0" + bindings "^1.3.0" + node-addon-api "^1.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +aria-query@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-types-flow@0.0.7, ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +axobject-query@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" + integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww== + dependencies: + ast-types-flow "0.0.7" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-eslint@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456" + integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-scope "3.7.1" + eslint-visitor-keys "^1.0.0" + +babel-generator@^6.18.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-jest@^23.6.0: + version "23.6.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1" + integrity sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew== + dependencies: + babel-plugin-istanbul "^4.1.6" + babel-preset-jest "^23.2.0" + +babel-jest@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589" + integrity sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw== + dependencies: + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.6.0" + chalk "^2.4.2" + slash "^2.0.0" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-istanbul@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45" + integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ== + dependencies: + babel-plugin-syntax-object-rest-spread "^6.13.0" + find-up "^2.1.0" + istanbul-lib-instrument "^1.10.1" + test-exclude "^4.2.1" + +babel-plugin-istanbul@^5.1.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz#841d16b9a58eeb407a0ddce622ba02fe87a752ba" + integrity sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ== + dependencies: + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + +babel-plugin-jest-hoist@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" + integrity sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc= + +babel-plugin-jest-hoist@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz#f7f7f7ad150ee96d7a5e8e2c5da8319579e78019" + integrity sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w== + dependencies: + "@types/babel__traverse" "^7.0.6" + +babel-plugin-syntax-object-rest-spread@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= + +babel-plugin-transform-es2015-modules-commonjs@^6.26.2: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" + integrity sha1-jsegOhOPABoaj7HoETZSvxpV2kY= + dependencies: + babel-plugin-jest-hoist "^23.2.0" + babel-plugin-syntax-object-rest-spread "^6.13.0" + +babel-preset-jest@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984" + integrity sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.6.0" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.16.0, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-0.8.2.tgz#c9b6bca08d1bc2ea00fc8afb4f1a5fd1e1c66e4e" + integrity sha1-yba8oI0bwuoA/Ir7Txpf0eHGbk4= + dependencies: + readable-stream "~1.0.26" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-fs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-fs/-/browserify-fs-1.0.0.tgz#f075aa8a729d4d1716d066620e386fcc1311a96f" + integrity sha1-8HWqinKdTRcW0GZiDjhvzBMRqW8= + dependencies: + level-filesystem "^1.0.1" + level-js "^2.1.3" + levelup "^0.18.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserslist@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.1.tgz#ee5059b1aec18cbec9d055d6cb5e24ae50343a9b" + integrity sha512-1MC18ooMPRG2UuVFJTHFIAkk6mpByJfxCrnUyvSlu/hyQSFHMrlhM02SzNuCV+quTP4CKmqtOMAIjrifrpBJXQ== + dependencies: + caniuse-lite "^1.0.30000971" + electron-to-chromium "^1.3.137" + node-releases "^1.1.21" + +bser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" + integrity sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk= + dependencies: + node-int64 "^0.4.0" + +buffer-es6@^4.9.2, buffer-es6@^4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/buffer-es6/-/buffer-es6-4.9.3.tgz#f26347b82df76fd37e18bcb5288c4970cfd5c404" + integrity sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +builtin-modules@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" + integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30000971: + version "1.0.30000974" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000974.tgz#b7afe14ee004e97ce6dc73e3f878290a12928ad8" + integrity sha512-xc3rkNS/Zc3CmpMKuczWEdY2sZgx09BkAxfvkxlAEBTqcMHeL8QnPqhKse+5sRTi3nrw2pJwToD2WvKn1Uhvww== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^2.0.4: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +clone@~0.1.9: + version "0.1.19" + resolved "https://registry.yarnpkg.com/clone/-/clone-0.1.19.tgz#613fb68639b26a494ac53253e15b1a6bd88ada85" + integrity sha1-YT+2hjmyaklKxTJT4Vsaa9iK2oU= + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.11.0, commander@^2.8.1, commander@~2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.4.4: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +confusing-browser-globals@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.7.tgz#5ae852bd541a910e7ffb2dbb864a2d21a36ad29b" + integrity sha512-cgHI1azax5ATrZ8rJ+ODDML9Fvu67PimB6aNxBrc/QwSaDaM9eTfIEUHx3bBLJJ82ioSb+/5zfsMCCEJax3ByQ== + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +convert-source-map@^1.1.0, convert-source-map@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== + dependencies: + safe-buffer "~5.1.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.1.3.tgz#0cc3ba4c7f62928c2837e1cffbe8dc78b4f1ae14" + integrity sha512-EP018pVhgwsKHz3YoN1hTq49aRe+h017Kjz0NQz3nXV0cCRMvH3fLQl+vEPGr4r4J5sk4sU3tUC7U1aqTCeJeA== + dependencies: + browserslist "^4.6.0" + core-js-pure "3.1.3" + semver "^6.1.0" + +core-js-pure@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.3.tgz#4c90752d5b9471f641514f3728f51c1e0783d0b5" + integrity sha512-k3JWTrcQBKqjkjI0bkfXS0lbpWPxYuHWfMMjC1VDmzU4Q58IwSbuXSo99YO/hUHlw/EB4AlfA2PVxOGkrIq6dA== + +core-js@^2.4.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-env@^5.1.4: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2" + integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg== + dependencies: + cross-spawn "^6.0.5" + is-windows "^1.0.0" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.6" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" + integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== + +cssstyle@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.2.tgz#427ea4d585b18624f6fdbf9de7a2a1a3ba713077" + integrity sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow== + dependencies: + cssom "0.3.x" + +damerau-levenshtein@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414" + integrity sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +date-fns@^1.29.0: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + +debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deferred-leveldown@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz#2cef1f111e1c57870d8bbb8af2650e587cd2f5b4" + integrity sha1-LO8fER4cV4cNi7uK8mUOWHzS9bQ= + dependencies: + abstract-leveldown "~0.12.1" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +diff-sequences@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" + integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +electron-to-chromium@^1.3.137: + version "1.3.150" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.150.tgz#afb972b53a702b37c76db843c39c967e9f68464b" + integrity sha512-5wuYlaXhXbBvavSTij5ZyidICB6sAK/1BwgZZoPCgsniid1oDgzVvDOV/Dw6J25lKV9QZ9ZdQCp8MEfF0/OIKA== + +elliptic@^6.0.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" + integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1, emoji-regex@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +errno@^0.1.1, errno@~0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.9.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-airbnb-base@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz#f6ea81459ff4dec2dda200c35f1d8f7419d57943" + integrity sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w== + dependencies: + confusing-browser-globals "^1.0.5" + object.assign "^4.1.0" + object.entries "^1.1.0" + +eslint-config-airbnb@^17.1.0: + version "17.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-17.1.1.tgz#2272e0b86bb1e2b138cdf88d07a3b6f4cda3d626" + integrity sha512-xCu//8a/aWqagKljt+1/qAM62BYZeNq04HmdevG5yUGWpja0I/xhqd6GdLRch5oetEGFiJAnvtGuTEAese53Qg== + dependencies: + eslint-config-airbnb-base "^13.2.0" + object.assign "^4.1.0" + object.entries "^1.1.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-module-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.14.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz#7a5ba8d32622fb35eb9c8db195c2090bd18a3678" + integrity sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig== + dependencies: + array-includes "^3.0.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.11.0" + +eslint-plugin-jsx-a11y@^6.1.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" + integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== + dependencies: + "@babel/runtime" "^7.4.5" + aria-query "^3.0.0" + array-includes "^3.0.3" + ast-types-flow "^0.0.7" + axobject-query "^2.0.2" + damerau-levenshtein "^1.0.4" + emoji-regex "^7.0.2" + has "^1.0.3" + jsx-ast-utils "^2.2.1" + +eslint-plugin-react@^7.11.0: + version "7.14.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.2.tgz#94c193cc77a899ac0ecbb2766fbef88685b7ecc1" + integrity sha512-jZdnKe3ip7FQOdjxks9XPN0pjUKZYq48OggNMd16Sk+8VXx6JOvXmlElxROCgp7tiUsTsze3jd78s/9AFJP2mA== + dependencies: + array-includes "^3.0.3" + doctrine "^2.1.0" + has "^1.0.3" + jsx-ast-utils "^2.1.0" + object.entries "^1.1.0" + object.fromentries "^2.0.0" + object.values "^1.1.0" + prop-types "^15.7.2" + resolve "^1.10.1" + +eslint-scope@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" + integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== + +eslint@^5.3.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + +estree-walker@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" + integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== + +estree-walker@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.0.tgz#5d865327c44a618dde5699f763891ae31f257dae" + integrity sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw== + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" + integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +expect@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.8.0.tgz#471f8ec256b7b6129ca2524b2a62f030df38718d" + integrity sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA== + dependencies: + "@jest/types" "^24.8.0" + ansi-styles "^3.2.0" + jest-get-type "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-regex-util "^24.3.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= + dependencies: + bser "^2.0.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +foreach@~2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fs-minipass@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + dependencies: + minipass "^2.2.1" + +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +fwd-stream@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fwd-stream/-/fwd-stream-1.0.4.tgz#ed281cabed46feecf921ee32dc4c50b372ac7cfa" + integrity sha1-7Sgcq+1G/uz5Ie4y3ExQs3KsfPo= + dependencies: + readable-stream "~1.0.26-4" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +handlebars@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" + integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== + dependencies: + neo-async "^2.6.0" + optimist "^0.6.1" + source-map "^0.6.1" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +idb-wrapper@^1.5.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/idb-wrapper/-/idb-wrapper-1.7.2.tgz#8251afd5e77fe95568b1c16152eb44b396767ea2" + integrity sha512-zfNREywMuf0NzDo9mVsL0yegjsirJxHpKHvWcyRozIqQy89g0a3U+oBPOCN4cc0oCiOuYgZHimzaW/R46G1Mpg== + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" + integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indexof@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inherits@^2.0.1, inherits@~2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@^6.2.2: + version "6.5.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" + integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-object@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7" + integrity sha1-AO+8CIFsM8/ErIJR0TLhDcZQmNc= + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-reference@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.2.tgz#01cf91517d21db66a34642287ed6e70d53dcbe5c" + integrity sha512-Kn5g8c7XHKejFOpTf2QN9YjiHHKl5xRj+2uAZf9iM2//nkBNi/NNeB5JMoun28nEaUVHyPUzqzhfRlfAirEjXg== + dependencies: + "@types/estree" "0.0.39" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.0, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is@~0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" + integrity sha1-OzSixI81mXLzUEKEkZOucmS2NWI= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isbuffer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b" + integrity sha1-OMFG2d9Si4v5sHAcPUPPEt8/w5s= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" + integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ== + +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-instrument@^1.10.1: + version "1.10.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca" + integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A== + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.1" + semver "^5.3.0" + +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.1.1: + version "2.2.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" + integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== + dependencies: + handlebars "^4.1.2" + +jest-changed-files@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b" + integrity sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug== + dependencies: + "@jest/types" "^24.8.0" + execa "^1.0.0" + throat "^4.0.0" + +jest-cli@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.8.0.tgz#b075ac914492ed114fa338ade7362a301693e989" + integrity sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA== + dependencies: + "@jest/core" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^12.0.2" + +jest-config@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.8.0.tgz#77db3d265a6f726294687cbbccc36f8a76ee0f4f" + integrity sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.8.0" + "@jest/types" "^24.8.0" + babel-jest "^24.8.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.8.0" + jest-environment-node "^24.8.0" + jest-get-type "^24.8.0" + jest-jasmine2 "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + micromatch "^3.1.10" + pretty-format "^24.8.0" + realpath-native "^1.1.0" + +jest-diff@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.8.0.tgz#146435e7d1e3ffdf293d53ff97e193f1d1546172" + integrity sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.3.0" + jest-get-type "^24.8.0" + pretty-format "^24.8.0" + +jest-docblock@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.3.0.tgz#b9c32dac70f72e4464520d2ba4aec02ab14db5dd" + integrity sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg== + dependencies: + detect-newline "^2.1.0" + +jest-each@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.8.0.tgz#a05fd2bf94ddc0b1da66c6d13ec2457f35e52775" + integrity sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA== + dependencies: + "@jest/types" "^24.8.0" + chalk "^2.0.1" + jest-get-type "^24.8.0" + jest-util "^24.8.0" + pretty-format "^24.8.0" + +jest-environment-jsdom@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz#300f6949a146cabe1c9357ad9e9ecf9f43f38857" + integrity sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/fake-timers" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + jest-util "^24.8.0" + jsdom "^11.5.1" + +jest-environment-node@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.8.0.tgz#d3f726ba8bc53087a60e7a84ca08883a4c892231" + integrity sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/fake-timers" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + jest-util "^24.8.0" + +jest-get-type@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.8.0.tgz#a7440de30b651f5a70ea3ed7ff073a32dfe646fc" + integrity sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ== + +jest-haste-map@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.8.0.tgz#51794182d877b3ddfd6e6d23920e3fe72f305800" + integrity sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ== + dependencies: + "@jest/types" "^24.8.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.4.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + +jest-jasmine2@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz#a9c7e14c83dd77d8b15e820549ce8987cc8cd898" + integrity sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.8.0" + is-generator-fn "^2.0.0" + jest-each "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-runtime "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + pretty-format "^24.8.0" + throat "^4.0.0" + +jest-leak-detector@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz#c0086384e1f650c2d8348095df769f29b48e6980" + integrity sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g== + dependencies: + pretty-format "^24.8.0" + +jest-matcher-utils@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz#2bce42204c9af12bde46f83dc839efe8be832495" + integrity sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw== + dependencies: + chalk "^2.0.1" + jest-diff "^24.8.0" + jest-get-type "^24.8.0" + pretty-format "^24.8.0" + +jest-message-util@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.8.0.tgz#0d6891e72a4beacc0292b638685df42e28d6218b" + integrity sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + +jest-mock@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.8.0.tgz#2f9d14d37699e863f1febf4e4d5a33b7fdbbde56" + integrity sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A== + dependencies: + "@jest/types" "^24.8.0" + +jest-pnp-resolver@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" + integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + +jest-regex-util@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.3.0.tgz#d5a65f60be1ae3e310d5214a0307581995227b36" + integrity sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg== + +jest-resolve-dependencies@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz#19eec3241f2045d3f990dba331d0d7526acff8e0" + integrity sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw== + dependencies: + "@jest/types" "^24.8.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.8.0" + +jest-resolve@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.8.0.tgz#84b8e5408c1f6a11539793e2b5feb1b6e722439f" + integrity sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw== + dependencies: + "@jest/types" "^24.8.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-runner@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.8.0.tgz#4f9ae07b767db27b740d7deffad0cf67ccb4c5bb" + integrity sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.8.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.8.0" + jest-jasmine2 "^24.8.0" + jest-leak-detector "^24.8.0" + jest-message-util "^24.8.0" + jest-resolve "^24.8.0" + jest-runtime "^24.8.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.8.0.tgz#05f94d5b05c21f6dc54e427cd2e4980923350620" + integrity sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.8.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/yargs" "^12.0.2" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.8.0" + jest-haste-map "^24.8.0" + jest-message-util "^24.8.0" + jest-mock "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^12.0.2" + +jest-serializer@^24.4.0: + version "24.4.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.4.0.tgz#f70c5918c8ea9235ccb1276d232e459080588db3" + integrity sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q== + +jest-snapshot@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.8.0.tgz#3bec6a59da2ff7bc7d097a853fb67f9d415cb7c6" + integrity sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + expect "^24.8.0" + jest-diff "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-resolve "^24.8.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.8.0" + semver "^5.5.0" + +jest-util@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.8.0.tgz#41f0e945da11df44cc76d64ffb915d0716f46cd1" + integrity sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA== + dependencies: + "@jest/console" "^24.7.1" + "@jest/fake-timers" "^24.8.0" + "@jest/source-map" "^24.3.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + +jest-validate@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.8.0.tgz#624c41533e6dfe356ffadc6e2423a35c2d3b4849" + integrity sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA== + dependencies: + "@jest/types" "^24.8.0" + camelcase "^5.0.0" + chalk "^2.0.1" + jest-get-type "^24.8.0" + leven "^2.1.0" + pretty-format "^24.8.0" + +jest-watcher@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.8.0.tgz#58d49915ceddd2de85e238f6213cef1c93715de4" + integrity sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw== + dependencies: + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/yargs" "^12.0.9" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.8.0" + string-length "^2.0.0" + +jest-worker@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.6.0.tgz#7f81ceae34b7cde0c9827a6980c35b7cdc0161b3" + integrity sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ== + dependencies: + merge-stream "^1.0.1" + supports-color "^6.1.0" + +jest@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.8.0.tgz#d5dff1984d0d1002196e9b7f12f75af1b2809081" + integrity sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg== + dependencies: + import-local "^2.0.0" + jest-cli "^24.8.0" + +js-levenshtein@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.13.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jsx-ast-utils@^2.1.0, jsx-ast-utils@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" + integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== + dependencies: + array-includes "^3.0.3" + object.assign "^4.1.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +kleur@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +level-blobs@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/level-blobs/-/level-blobs-0.1.7.tgz#9ab9b97bb99f1edbf9f78a3433e21ed56386bdaf" + integrity sha1-mrm5e7mfHtv594o0M+Ie1WOGva8= + dependencies: + level-peek "1.0.6" + once "^1.3.0" + readable-stream "^1.0.26-4" + +level-filesystem@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/level-filesystem/-/level-filesystem-1.2.0.tgz#a00aca9919c4a4dfafdca6a8108d225aadff63b3" + integrity sha1-oArKmRnEpN+v3KaoEI0iWq3/Y7M= + dependencies: + concat-stream "^1.4.4" + errno "^0.1.1" + fwd-stream "^1.0.4" + level-blobs "^0.1.7" + level-peek "^1.0.6" + level-sublevel "^5.2.0" + octal "^1.0.0" + once "^1.3.0" + xtend "^2.2.0" + +level-fix-range@2.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-fix-range/-/level-fix-range-2.0.0.tgz#c417d62159442151a19d9a2367868f1724c2d548" + integrity sha1-xBfWIVlEIVGhnZojZ4aPFyTC1Ug= + dependencies: + clone "~0.1.9" + +level-fix-range@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/level-fix-range/-/level-fix-range-1.0.2.tgz#bf15b915ae36d8470c821e883ddf79cd16420828" + integrity sha1-vxW5Fa422EcMgh6IPd95zRZCCCg= + +"level-hooks@>=4.4.0 <5": + version "4.5.0" + resolved "https://registry.yarnpkg.com/level-hooks/-/level-hooks-4.5.0.tgz#1b9ae61922930f3305d1a61fc4d83c8102c0dd93" + integrity sha1-G5rmGSKTDzMF0aYfxNg8gQLA3ZM= + dependencies: + string-range "~1.2" + +level-js@^2.1.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-2.2.4.tgz#bc055f4180635d4489b561c9486fa370e8c11697" + integrity sha1-vAVfQYBjXUSJtWHJSG+jcOjBFpc= + dependencies: + abstract-leveldown "~0.12.0" + idb-wrapper "^1.5.0" + isbuffer "~0.0.0" + ltgt "^2.1.2" + typedarray-to-buffer "~1.0.0" + xtend "~2.1.2" + +level-peek@1.0.6, level-peek@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/level-peek/-/level-peek-1.0.6.tgz#bec51c72a82ee464d336434c7c876c3fcbcce77f" + integrity sha1-vsUccqgu5GTTNkNMfIdsP8vM538= + dependencies: + level-fix-range "~1.0.2" + +level-sublevel@^5.2.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-5.2.3.tgz#744c12c72d2e72be78dde3b9b5cd84d62191413a" + integrity sha1-dEwSxy0ucr543eO5tc2E1iGRQTo= + dependencies: + level-fix-range "2.0" + level-hooks ">=4.4.0 <5" + string-range "~1.2.1" + xtend "~2.0.4" + +levelup@^0.18.2: + version "0.18.6" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-0.18.6.tgz#e6a01cb089616c8ecc0291c2a9bd3f0c44e3e5eb" + integrity sha1-5qAcsIlhbI7MApHCqb0/DETj5es= + dependencies: + bl "~0.8.1" + deferred-leveldown "~0.2.0" + errno "~0.1.1" + prr "~0.0.0" + readable-stream "~1.0.26" + semver "~2.3.1" + xtend "~3.0.0" + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash@^4.17.11, lodash@^4.17.4: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +lodash@^4.17.12: + 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, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +lunr@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.6.tgz#f278beee7ffd56ad86e6e478ce02ab2b98c78dd5" + integrity sha512-swStvEyDqQ85MGpABCMBclZcLI/pBIlu8FFDtmX197+oEgKloJ67QnB+Tidh0340HmLMs39c4GrkPY3cmkXp6Q== + +magic-string@^0.22.5: + version "0.22.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" + integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== + dependencies: + vlq "^0.2.2" + +magic-string@^0.25.2: + version "0.25.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" + integrity sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.1, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +minipass@^2.2.1, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanoid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.0.1.tgz#deb55cac196e3f138071911dabbc3726eb048864" + integrity sha512-k1u2uemjIGsn25zmujKnotgniC/gxQ9sdegdezeDiKdkDW56THUMqlz3urndKCXJxA6yPzSZbXx/QCMe/pxqsA== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +neo-async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^1.6.0: + version "1.6.3" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.3.tgz#3998d4593e2dca2ea82114670a4eb003386a9fe1" + integrity sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^5.2.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.0.tgz#7b455fdce9f7de0c63538297354f3db468426e6a" + integrity sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.1.21: + version "1.1.23" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.23.tgz#de7409f72de044a2fa59c097f436ba89c39997f0" + integrity sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w== + dependencies: + semver "^5.3.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.0.7: + version "2.1.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.4.tgz#e006a878db23636f8e8a67d33ca0e4edf61a842f" + integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.2.0.tgz#cddec02998b091be42bf1035ae32e49f1cb6ea67" + integrity sha1-zd7AKZiwkb5CvxA1rjLknxy26mc= + dependencies: + foreach "~2.0.1" + indexof "~0.0.1" + is "~0.2.6" + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.entries@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.11.0" + function-bind "^1.1.1" + has "^1.0.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +octal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/octal/-/octal-1.0.0.tgz#63e7162a68efbeb9e213588d58e989d1e5c4530b" + integrity sha1-Y+cWKmjvvrniE1iNWOmJ0eXEUws= + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1, optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +output-file-sync@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-2.0.1.tgz#f53118282f5f553c2799541792b723a4c71430c0" + integrity sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ== + dependencies: + graceful-fs "^4.1.11" + is-plain-obj "^1.1.0" + mkdirp "^0.5.1" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +pretty-format@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" + integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== + dependencies: + "@jest/types" "^24.8.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-es6@^0.11.2, process-es6@^0.11.6: + version "0.11.6" + resolved "https://registry.yarnpkg.com/process-es6/-/process-es6-0.11.6.tgz#c6bb389f9a951f82bd4eb169600105bd2ff9c778" + integrity sha1-xrs4n5qVH4K9TrFpYAEFvS/5x3g= + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prompts@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.1.0.tgz#bf90bc71f6065d255ea2bdc0fe6520485c1b45db" + integrity sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg== + dependencies: + kleur "^3.0.2" + sisteransi "^1.0.0" + +prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + integrity sha1-GoS4WQgyVQFBGFPQCB7j+obikmo= + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.24, psl@^1.1.28: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-is@^16.8.1, react-is@^16.8.4: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readable-stream@^1.0.26-4: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" + integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.26, readable-stream@~1.0.26-4: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + +regenerate-unicode-properties@^8.0.2: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + +regenerator-transform@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.0.tgz#2ca9aaf7a2c239dd32e4761218425b8c7a86ecaf" + integrity sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w== + dependencies: + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp-tree@^0.1.6: + version "0.1.10" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.10.tgz#d837816a039c7af8a8d64d7a7c3cf6a1d93450bc" + integrity sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpu-core@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" + integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.0.2" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + dependencies: + lodash "^4.17.11" + +request-promise-native@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== + dependencies: + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@^1.10.0, resolve@^1.10.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" + integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw== + dependencies: + path-parse "^1.0.6" + +resolve@^1.11.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rollup-plugin-commonjs@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.0.0.tgz#58901ebe7ca44c2a03f0056de9bf9eb4a2dc8990" + integrity sha512-B8MoX5GRpj3kW4+YaFO/di2JsZkBxNjVmZ9LWjUoTAjq8N9wc7HObMXPsrvolVV9JXVtYSscflXM14A19dXPNQ== + dependencies: + estree-walker "^0.6.0" + is-reference "^1.1.2" + magic-string "^0.25.2" + resolve "^1.10.1" + rollup-pluginutils "^2.7.0" + +rollup-plugin-node-builtins@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz#24a1fed4a43257b6b64371d8abc6ce1ab14597e9" + integrity sha1-JKH+1KQyV7a2Q3HYq8bOGrFFl+k= + dependencies: + browserify-fs "^1.0.0" + buffer-es6 "^4.9.2" + crypto-browserify "^3.11.0" + process-es6 "^0.11.2" + +rollup-plugin-node-globals@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-globals/-/rollup-plugin-node-globals-1.4.0.tgz#5e1f24a9bb97c0ef51249f625e16c7e61b7c020b" + integrity sha512-xRkB+W/m1KLIzPUmG0ofvR+CPNcvuCuNdjVBVS7ALKSxr3EDhnzNceGkGi1m8MToSli13AzKFYH4ie9w3I5L3g== + dependencies: + acorn "^5.7.3" + buffer-es6 "^4.9.3" + estree-walker "^0.5.2" + magic-string "^0.22.5" + process-es6 "^0.11.6" + rollup-pluginutils "^2.3.1" + +rollup-plugin-node-resolve@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.0.0.tgz#754abf4841ed4bab2241551cba0a11d04c57f290" + integrity sha512-JUFr7DkFps3div9DYwpSg0O+s8zuSSRASUZUVNx6h6zhw2m8vcpToeS68JDPsFbmisMVSMYK0IxftngCRv7M9Q== + dependencies: + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.10.1" + rollup-pluginutils "^2.7.0" + +rollup-pluginutils@^2.3.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97" + integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg== + dependencies: + estree-walker "^0.6.1" + +rollup-pluginutils@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.7.0.tgz#da24a6f99432626160643ac6b087d71684317c4f" + integrity sha512-FoP6L1YnMYTAR06Dpq5LE3jJtMwPE6H4VEOqFU23yoziZnqNRSiWcVy6YgEY5PdQB4G7278+8c4TvB0JKS1csA== + dependencies: + estree-walker "^0.6.0" + micromatch "^3.1.10" + +rollup@^1.12.0: + version "1.12.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.12.1.tgz#02148d3d79f819b418a2f4c374f859fed56ae40c" + integrity sha512-6CL0c+HxqEY9/8G+7mYIvor0tt60mVC64410/1DeaGWZCFAvcY8ClT0bsnTxECd6vR004CMm0X2yBZmod5gWjw== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "^12.0.2" + acorn "^6.1.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +rxjs@^6.4.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" + integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, 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.0, safe-buffer@^5.1.1: + 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" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +semver@^6.0.0, semver@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" + integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== + +semver@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" + integrity sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI= + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +shortid@^2.2.8: + version "2.2.14" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.14.tgz#80db6aafcbc3e3a46850b3c88d39e051b84c8d18" + integrity sha512-4UnZgr9gDdA1kaKj/38IiudfC3KHKhDc1zi/HSxd9FQDR0VLwH3/y79tZJLsVYPsJgIjeHjqIWaWVRJUj9qZOQ== + dependencies: + nanoid "^2.0.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +sisteransi@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" + integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" + integrity sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg== + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-range@~1.2, string-range@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" + integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +symbol-tree@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= + +table@^5.2.3: + version "5.4.1" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.1.tgz#0691ae2ebe8259858efb63e550b6d5f9300171e8" + integrity sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w== + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tar@^4: + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.5" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +test-exclude@^4.2.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20" + integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA== + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@^2.3.3, tough-cookie@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +typedarray-to-buffer@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz#9bb8ba0e841fb3f4cf1fe7c245e9f3fa8a5fe99c" + integrity sha1-m7i6DoQfs/TPH+fCRenz+opf6Zw= + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uglify-js@^3.1.4: + version "3.5.12" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.12.tgz#6b759cabc08c3e91fe82323d6387019f0c5864cd" + integrity sha512-KeQesOpPiZNgVwJj8Ge3P4JYbQHUdZzpx6Fahy6eKAYRSV4zhVmLXoC+JtOeYxcHCHTve8RG1ZGdTvpeOUM26Q== + dependencies: + commander "~2.20.0" + source-map "~0.6.1" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vlq@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" + integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9, which@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xtend@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.2.0.tgz#eef6b1f198c1c8deafad8b1765a04dad4a01c5a9" + integrity sha1-7vax8ZjByN6vrYsXZaBNrUoBxak= + +xtend@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.0.6.tgz#5ea657a6dba447069c2e59c58a1138cb0c5e6cee" + integrity sha1-XqZXptukRwacLlnFihE4ywxebO4= + dependencies: + is-object "~0.1.2" + object-keys "~0.2.0" + +xtend@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +xtend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" + integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= + +"y18n@^3.2.1 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^3.0.0, yallist@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^12.0.2: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1"