From e55120bb62a20d2b09241f23b144448dea6f2757 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 6 May 2020 20:49:21 +0100 Subject: [PATCH] removed core library --- .../builder/src/components/common/core.js | 6 +- packages/core/.babelrc | 12 - packages/core/.gitignore | 43 - packages/core/.npmignore | 2 - packages/core/.travis.yml | 11 - packages/core/.vscode/launch.json | 14 - packages/core/.vscode/settings.json | 0 packages/core/CONTRIBUTING.md | 22 - packages/core/LICENSE | 373 -- packages/core/package.json | 83 - packages/core/readme.md | 21 - packages/core/rollup.config.js | 153 - .../src/actionsApi/buildBehaviourSource.js | 15 - packages/core/src/actionsApi/execute.js | 17 - packages/core/src/actionsApi/index.js | 7 - packages/core/src/actionsApi/initialise.js | 103 - packages/core/src/appInitialise/cloneApp.js | 8 - .../core/src/appInitialise/eventAggregator.js | 27 - packages/core/src/appInitialise/index.js | 13 - .../core/src/appInitialise/initialiseData.js | 55 - packages/core/src/authApi/authCommon.js | 53 - packages/core/src/authApi/authenticate.js | 118 - .../core/src/authApi/createTemporaryAccess.js | 70 - packages/core/src/authApi/createUser.js | 96 - packages/core/src/authApi/enableUser.js | 58 - .../src/authApi/generateFullPermissions.js | 36 - .../core/src/authApi/getNewAccessLevel.js | 5 - packages/core/src/authApi/getNewUser.js | 35 - packages/core/src/authApi/getUsers.js | 19 - packages/core/src/authApi/index.js | 46 - packages/core/src/authApi/isAuthorized.js | 44 - packages/core/src/authApi/loadAccessLevels.js | 16 - packages/core/src/authApi/permissions.js | 81 - packages/core/src/authApi/saveAccessLevels.js | 52 - packages/core/src/authApi/setPassword.js | 160 - .../core/src/authApi/setUserAccessLevels.js | 60 - .../core/src/authApi/validateAccessLevels.js | 93 - packages/core/src/authApi/validateUser.js | 51 - packages/core/src/collectionApi/delete.js | 57 - .../collectionApi/getAllowedRecordTypes.js | 20 - packages/core/src/collectionApi/index.js | 11 - packages/core/src/collectionApi/initialise.js | 48 - packages/core/src/common/apiWrapper.js | 128 - packages/core/src/common/compileCode.js | 26 - packages/core/src/common/errors.js | 34 - packages/core/src/common/events.js | 74 - packages/core/src/common/index.js | 317 - packages/core/src/common/lock.js | 109 - packages/core/src/common/nodeCrypto.js | 14 - packages/core/src/common/validationCommon.js | 14 - packages/core/src/index.js | 112 - packages/core/src/indexApi/aggregates.js | 180 - packages/core/src/indexApi/buildIndex.js | 143 - packages/core/src/indexApi/delete.js | 38 - packages/core/src/indexApi/getIndexDir.js | 14 - packages/core/src/indexApi/index.js | 11 - packages/core/src/indexApi/listItems.js | 76 - packages/core/src/indexing/allIds.js | 279 - packages/core/src/indexing/apply.js | 107 - packages/core/src/indexing/evaluate.js | 85 - .../core/src/indexing/indexSchemaCreator.js | 58 - packages/core/src/indexing/initialiseIndex.js | 27 - .../src/indexing/promiseReadableStream.js | 80 - .../src/indexing/promiseWritableStream.js | 117 - packages/core/src/indexing/read.js | 93 - packages/core/src/indexing/relevant.js | 126 - packages/core/src/indexing/serializer.js | 238 - packages/core/src/indexing/sharding.js | 116 - packages/core/src/recordApi/customId.js | 29 - packages/core/src/recordApi/delete.js | 35 - packages/core/src/recordApi/downloadFile.js | 31 - packages/core/src/recordApi/getContext.js | 76 - packages/core/src/recordApi/getNew.js | 51 - packages/core/src/recordApi/index.js | 27 - .../core/src/recordApi/initialiseChildren.js | 79 - packages/core/src/recordApi/load.js | 77 - packages/core/src/recordApi/recordInfo.js | 112 - packages/core/src/recordApi/save.js | 65 - packages/core/src/recordApi/uploadFile.js | 146 - packages/core/src/recordApi/validate.js | 91 - .../core/src/templateApi/canDeleteIndex.js | 54 - .../core/src/templateApi/canDeleteModel.js | 45 - .../core/src/templateApi/createActions.js | 22 - packages/core/src/templateApi/createNodes.js | 242 - .../templateApi/deleteAllIndexFilesForNode.js | 30 - .../templateApi/deleteAllRecordsForNode.js | 33 - packages/core/src/templateApi/deleteNodes.js | 9 - .../core/src/templateApi/diffHierarchy.js | 203 - packages/core/src/templateApi/fields.js | 96 - .../templateApi/getApplicationDefinition.js | 12 - .../src/templateApi/getBehaviourSources.js | 3 - .../src/templateApi/getCouchDbMapFunction.js | 32 - packages/core/src/templateApi/hierarchy.js | 290 - packages/core/src/templateApi/index.js | 68 - packages/core/src/templateApi/indexes.js | 55 - .../src/templateApi/initialiseNewIndex.js | 27 - .../src/templateApi/recordValidationRules.js | 46 - .../src/templateApi/saveActionsAndTriggers.js | 52 - .../templateApi/saveApplicationHierarchy.js | 45 - packages/core/src/templateApi/upgradeData.js | 208 - packages/core/src/templateApi/validate.js | 215 - .../core/src/templateApi/validateAggregate.js | 23 - packages/core/src/transactions/cleanup.js | 56 - packages/core/src/transactions/create.js | 91 - packages/core/src/transactions/execute.js | 359 -- packages/core/src/transactions/retrieve.js | 215 - .../core/src/transactions/setCleanupFunc.js | 14 - .../src/transactions/transactionsCommon.js | 52 - packages/core/src/types/array.js | 68 - packages/core/src/types/bool.js | 47 - packages/core/src/types/datetime.js | 82 - packages/core/src/types/file.js | 56 - packages/core/src/types/index.js | 85 - packages/core/src/types/number.js | 94 - packages/core/src/types/object.js | 59 - packages/core/src/types/reference.js | 91 - packages/core/src/types/string.js | 74 - packages/core/src/types/typeHelpers.js | 94 - packages/core/test/actionsApi.execute.spec.js | 112 - packages/core/test/apiWrapper.spec.js | 169 - .../core/test/authApi.authenticate.spec.js | 144 - .../core/test/authApi.changePassword.spec.js | 160 - .../test/authApi.createAccessLevels.spec.js | 214 - packages/core/test/authApi.createUser.spec.js | 215 - .../core/test/authApi.disableUser.spec.js | 157 - .../test/authApi.setUserAccesslLevels.spec.js | 84 - .../core/test/collectionApi.delete.spec.js | 81 - packages/core/test/collectionApi.spec.js | 200 - packages/core/test/common.spec.js | 216 - packages/core/test/couchDb.js | 107 - packages/core/test/getApis.spec.js | 88 - .../core/test/indexApi.aggregates.spec.js | 159 - .../core/test/indexApi.buildIndex.spec.js | 610 -- packages/core/test/indexApi.list.spec.js | 161 - .../core/test/indexing.concurrency.spec.js | 288 - .../test/indexing.createIndexFile.spec.js | 22 - packages/core/test/indexing.evaluate.spec.js | 131 - .../test/indexing.getRelevantIndexes.spec.js | 170 - packages/core/test/indexing.schema.spec.js | 134 - packages/core/test/initialiseData.spec.js | 86 - packages/core/test/memory.js | 178 - packages/core/test/recordApi.customId.spec.js | 38 - packages/core/test/recordApi.delete.spec.js | 73 - packages/core/test/recordApi.files.spec.js | 110 - .../core/test/recordApi.getContext.spec.js | 101 - packages/core/test/recordApi.getNew.spec.js | 113 - .../core/test/recordApi.getRecordInfo.spec.js | 155 - packages/core/test/recordApi.reindex.spec.js | 646 -- packages/core/test/recordApi.save.spec.js | 267 - packages/core/test/recordApi.validate.spec.js | 299 - packages/core/test/specHelpers.js | 648 -- packages/core/test/support/jasmine.json | 11 - .../templateApi.actionsValidation.spec.js | 110 - .../core/test/templateApi.canDelete.spec.js | 86 - .../templateApi.constructHeirarchy.spec.js | 184 - .../test/templateApi.diffHierarchy.spec.js | 269 - packages/core/test/templateApi.fields.spec.js | 124 - .../templateApi.heirarchyValidation.spec.js | 513 -- .../templateApi.loadSaveHeirarchy.spec.js | 181 - packages/core/test/templateApi.types.spec.js | 285 - .../core/test/templateApi.upgradeData.spec.js | 327 - packages/core/test/upgradeDataSetup.js | 52 - packages/core/yarn.lock | 5725 ----------------- packages/server/api/controllers/auth.js | 5 +- 164 files changed, 5 insertions(+), 23199 deletions(-) delete mode 100644 packages/core/.babelrc delete mode 100644 packages/core/.gitignore delete mode 100644 packages/core/.npmignore delete mode 100644 packages/core/.travis.yml delete mode 100644 packages/core/.vscode/launch.json delete mode 100644 packages/core/.vscode/settings.json delete mode 100644 packages/core/CONTRIBUTING.md delete mode 100644 packages/core/LICENSE delete mode 100644 packages/core/package.json delete mode 100644 packages/core/readme.md delete mode 100644 packages/core/rollup.config.js delete mode 100644 packages/core/src/actionsApi/buildBehaviourSource.js delete mode 100644 packages/core/src/actionsApi/execute.js delete mode 100644 packages/core/src/actionsApi/index.js delete mode 100644 packages/core/src/actionsApi/initialise.js delete mode 100644 packages/core/src/appInitialise/cloneApp.js delete mode 100644 packages/core/src/appInitialise/eventAggregator.js delete mode 100644 packages/core/src/appInitialise/index.js delete mode 100644 packages/core/src/appInitialise/initialiseData.js delete mode 100644 packages/core/src/authApi/authCommon.js delete mode 100644 packages/core/src/authApi/authenticate.js delete mode 100644 packages/core/src/authApi/createTemporaryAccess.js delete mode 100644 packages/core/src/authApi/createUser.js delete mode 100644 packages/core/src/authApi/enableUser.js delete mode 100644 packages/core/src/authApi/generateFullPermissions.js delete mode 100644 packages/core/src/authApi/getNewAccessLevel.js delete mode 100644 packages/core/src/authApi/getNewUser.js delete mode 100644 packages/core/src/authApi/getUsers.js delete mode 100644 packages/core/src/authApi/index.js delete mode 100644 packages/core/src/authApi/isAuthorized.js delete mode 100644 packages/core/src/authApi/loadAccessLevels.js delete mode 100644 packages/core/src/authApi/permissions.js delete mode 100644 packages/core/src/authApi/saveAccessLevels.js delete mode 100644 packages/core/src/authApi/setPassword.js delete mode 100644 packages/core/src/authApi/setUserAccessLevels.js delete mode 100644 packages/core/src/authApi/validateAccessLevels.js delete mode 100644 packages/core/src/authApi/validateUser.js delete mode 100644 packages/core/src/collectionApi/delete.js delete mode 100644 packages/core/src/collectionApi/getAllowedRecordTypes.js delete mode 100644 packages/core/src/collectionApi/index.js delete mode 100644 packages/core/src/collectionApi/initialise.js delete mode 100644 packages/core/src/common/apiWrapper.js delete mode 100644 packages/core/src/common/compileCode.js delete mode 100644 packages/core/src/common/errors.js delete mode 100644 packages/core/src/common/events.js delete mode 100644 packages/core/src/common/index.js delete mode 100644 packages/core/src/common/lock.js delete mode 100644 packages/core/src/common/nodeCrypto.js delete mode 100644 packages/core/src/common/validationCommon.js delete mode 100644 packages/core/src/index.js delete mode 100644 packages/core/src/indexApi/aggregates.js delete mode 100644 packages/core/src/indexApi/buildIndex.js delete mode 100644 packages/core/src/indexApi/delete.js delete mode 100644 packages/core/src/indexApi/getIndexDir.js delete mode 100644 packages/core/src/indexApi/index.js delete mode 100644 packages/core/src/indexApi/listItems.js delete mode 100644 packages/core/src/indexing/allIds.js delete mode 100644 packages/core/src/indexing/apply.js delete mode 100644 packages/core/src/indexing/evaluate.js delete mode 100644 packages/core/src/indexing/indexSchemaCreator.js delete mode 100644 packages/core/src/indexing/initialiseIndex.js delete mode 100644 packages/core/src/indexing/promiseReadableStream.js delete mode 100644 packages/core/src/indexing/promiseWritableStream.js delete mode 100644 packages/core/src/indexing/read.js delete mode 100644 packages/core/src/indexing/relevant.js delete mode 100644 packages/core/src/indexing/serializer.js delete mode 100644 packages/core/src/indexing/sharding.js delete mode 100644 packages/core/src/recordApi/customId.js delete mode 100644 packages/core/src/recordApi/delete.js delete mode 100644 packages/core/src/recordApi/downloadFile.js delete mode 100644 packages/core/src/recordApi/getContext.js delete mode 100644 packages/core/src/recordApi/getNew.js delete mode 100644 packages/core/src/recordApi/index.js delete mode 100644 packages/core/src/recordApi/initialiseChildren.js delete mode 100644 packages/core/src/recordApi/load.js delete mode 100644 packages/core/src/recordApi/recordInfo.js delete mode 100644 packages/core/src/recordApi/save.js delete mode 100644 packages/core/src/recordApi/uploadFile.js delete mode 100644 packages/core/src/recordApi/validate.js delete mode 100644 packages/core/src/templateApi/canDeleteIndex.js delete mode 100644 packages/core/src/templateApi/canDeleteModel.js delete mode 100644 packages/core/src/templateApi/createActions.js delete mode 100644 packages/core/src/templateApi/createNodes.js delete mode 100644 packages/core/src/templateApi/deleteAllIndexFilesForNode.js delete mode 100644 packages/core/src/templateApi/deleteAllRecordsForNode.js delete mode 100644 packages/core/src/templateApi/deleteNodes.js delete mode 100644 packages/core/src/templateApi/diffHierarchy.js delete mode 100644 packages/core/src/templateApi/fields.js delete mode 100644 packages/core/src/templateApi/getApplicationDefinition.js delete mode 100644 packages/core/src/templateApi/getBehaviourSources.js delete mode 100644 packages/core/src/templateApi/getCouchDbMapFunction.js delete mode 100644 packages/core/src/templateApi/hierarchy.js delete mode 100644 packages/core/src/templateApi/index.js delete mode 100644 packages/core/src/templateApi/indexes.js delete mode 100644 packages/core/src/templateApi/initialiseNewIndex.js delete mode 100644 packages/core/src/templateApi/recordValidationRules.js delete mode 100644 packages/core/src/templateApi/saveActionsAndTriggers.js delete mode 100644 packages/core/src/templateApi/saveApplicationHierarchy.js delete mode 100644 packages/core/src/templateApi/upgradeData.js delete mode 100644 packages/core/src/templateApi/validate.js delete mode 100644 packages/core/src/templateApi/validateAggregate.js delete mode 100644 packages/core/src/transactions/cleanup.js delete mode 100644 packages/core/src/transactions/create.js delete mode 100644 packages/core/src/transactions/execute.js delete mode 100644 packages/core/src/transactions/retrieve.js delete mode 100644 packages/core/src/transactions/setCleanupFunc.js delete mode 100644 packages/core/src/transactions/transactionsCommon.js delete mode 100644 packages/core/src/types/array.js delete mode 100644 packages/core/src/types/bool.js delete mode 100644 packages/core/src/types/datetime.js delete mode 100644 packages/core/src/types/file.js delete mode 100644 packages/core/src/types/index.js delete mode 100644 packages/core/src/types/number.js delete mode 100644 packages/core/src/types/object.js delete mode 100644 packages/core/src/types/reference.js delete mode 100644 packages/core/src/types/string.js delete mode 100644 packages/core/src/types/typeHelpers.js delete mode 100644 packages/core/test/actionsApi.execute.spec.js delete mode 100644 packages/core/test/apiWrapper.spec.js delete mode 100644 packages/core/test/authApi.authenticate.spec.js delete mode 100644 packages/core/test/authApi.changePassword.spec.js delete mode 100644 packages/core/test/authApi.createAccessLevels.spec.js delete mode 100644 packages/core/test/authApi.createUser.spec.js delete mode 100644 packages/core/test/authApi.disableUser.spec.js delete mode 100644 packages/core/test/authApi.setUserAccesslLevels.spec.js delete mode 100644 packages/core/test/collectionApi.delete.spec.js delete mode 100644 packages/core/test/collectionApi.spec.js delete mode 100644 packages/core/test/common.spec.js delete mode 100644 packages/core/test/couchDb.js delete mode 100644 packages/core/test/getApis.spec.js delete mode 100644 packages/core/test/indexApi.aggregates.spec.js delete mode 100644 packages/core/test/indexApi.buildIndex.spec.js delete mode 100644 packages/core/test/indexApi.list.spec.js delete mode 100644 packages/core/test/indexing.concurrency.spec.js delete mode 100644 packages/core/test/indexing.createIndexFile.spec.js delete mode 100644 packages/core/test/indexing.evaluate.spec.js delete mode 100644 packages/core/test/indexing.getRelevantIndexes.spec.js delete mode 100644 packages/core/test/indexing.schema.spec.js delete mode 100644 packages/core/test/initialiseData.spec.js delete mode 100644 packages/core/test/memory.js delete mode 100644 packages/core/test/recordApi.customId.spec.js delete mode 100644 packages/core/test/recordApi.delete.spec.js delete mode 100644 packages/core/test/recordApi.files.spec.js delete mode 100644 packages/core/test/recordApi.getContext.spec.js delete mode 100644 packages/core/test/recordApi.getNew.spec.js delete mode 100644 packages/core/test/recordApi.getRecordInfo.spec.js delete mode 100644 packages/core/test/recordApi.reindex.spec.js delete mode 100644 packages/core/test/recordApi.save.spec.js delete mode 100644 packages/core/test/recordApi.validate.spec.js delete mode 100644 packages/core/test/specHelpers.js delete mode 100644 packages/core/test/support/jasmine.json delete mode 100644 packages/core/test/templateApi.actionsValidation.spec.js delete mode 100644 packages/core/test/templateApi.canDelete.spec.js delete mode 100644 packages/core/test/templateApi.constructHeirarchy.spec.js delete mode 100644 packages/core/test/templateApi.diffHierarchy.spec.js delete mode 100644 packages/core/test/templateApi.fields.spec.js delete mode 100644 packages/core/test/templateApi.heirarchyValidation.spec.js delete mode 100644 packages/core/test/templateApi.loadSaveHeirarchy.spec.js delete mode 100644 packages/core/test/templateApi.types.spec.js delete mode 100644 packages/core/test/templateApi.upgradeData.spec.js delete mode 100644 packages/core/test/upgradeDataSetup.js delete mode 100644 packages/core/yarn.lock diff --git a/packages/builder/src/components/common/core.js b/packages/builder/src/components/common/core.js index d4f8fab89f..402623d7dc 100644 --- a/packages/builder/src/components/common/core.js +++ b/packages/builder/src/components/common/core.js @@ -1,5 +1,3 @@ -import { common } from "../../../../core/src" +import { flow } from "lodash/fp"; -export const pipe = common.$ - -export const events = common.eventsList \ No newline at end of file +export const pipe = (arg, funcs) => flow(funcs)(arg) diff --git a/packages/core/.babelrc b/packages/core/.babelrc deleted file mode 100644 index c52b9d492a..0000000000 --- a/packages/core/.babelrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "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/.gitignore b/packages/core/.gitignore deleted file mode 100644 index 57bf2e6855..0000000000 --- a/packages/core/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ - -# 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 - -# OSX -.DS_Store - -# flow-typed -flow-typed/npm/* -!flow-typed/npm/module_vx.x.x.js - - -.idea -npm-debug.log.* -dist diff --git a/packages/core/.npmignore b/packages/core/.npmignore deleted file mode 100644 index 9fdaeccbc3..0000000000 --- a/packages/core/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!dist/* diff --git a/packages/core/.travis.yml b/packages/core/.travis.yml deleted file mode 100644 index 56e1192d3b..0000000000 --- a/packages/core/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index b048bd4f3d..0000000000 --- a/packages/core/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // 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 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/core/CONTRIBUTING.md b/packages/core/CONTRIBUTING.md deleted file mode 100644 index 4d1ae8559d..0000000000 --- a/packages/core/CONTRIBUTING.md +++ /dev/null @@ -1,22 +0,0 @@ -### 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 deleted file mode 100644 index a612ad9813..0000000000 --- a/packages/core/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -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.json b/packages/core/package.json deleted file mode 100644 index 01dd18a693..0000000000 --- a/packages/core/package.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "@budibase/core", - "version": "0.0.32", - "description": "core javascript library for budibase", - "main": "dist/budibase-core.umd.js", - "module": "dist/budibase-core.esm.js", - "files": [ - "dist/**", - "!dist/node_modules" - ], - "directories": { - "test": "test" - }, - "scripts": { - "test": "jest", - "test:watch": "jest --watch", - "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" - ], - "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", - "babel-jest": "^23.6.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", - "cross-env": "^5.1.4", - "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", - "bcryptjs": "^2.4.3", - "date-fns": "^1.29.0", - "lodash": "^4.17.13", - "lunr": "^2.3.5", - "nano": "^8.2.2", - "safe-buffer": "^5.1.2", - "shortid": "^2.2.8" - }, - "devEngines": { - "node": ">=7.x", - "npm": ">=4.x", - "yarn": ">=0.21.3" - }, - "gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455" -} diff --git a/packages/core/readme.md b/packages/core/readme.md deleted file mode 100644 index 2d1e02970e..0000000000 --- a/packages/core/readme.md +++ /dev/null @@ -1,21 +0,0 @@ -## Getting Started - -Install packages: - -`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 deleted file mode 100644 index 547c465f4a..0000000000 --- a/packages/core/rollup.config.js +++ /dev/null @@ -1,153 +0,0 @@ -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", -} - -export default { - input: "src/index.js", - output: [ - { - file: "dist/budibase-core.cjs.js", - format: "cjs", - sourcemap: "inline", - }, - { - file: "dist/budibase-core.esm.mjs", - format: "esm", - sourcemap: "inline", - }, - { - 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", - "bcryptjs", - ], -} diff --git a/packages/core/src/actionsApi/buildBehaviourSource.js b/packages/core/src/actionsApi/buildBehaviourSource.js deleted file mode 100644 index 3e814d19eb..0000000000 --- a/packages/core/src/actionsApi/buildBehaviourSource.js +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index baeb878e87..0000000000 --- a/packages/core/src/actionsApi/execute.js +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 398aa661fa..0000000000 --- a/packages/core/src/actionsApi/index.js +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 90b27ac2b1..0000000000 --- a/packages/core/src/actionsApi/initialise.js +++ /dev/null @@ -1,103 +0,0 @@ -import { - isFunction, - filter, - map, - uniqBy, - keys, - difference, - join, - reduce, - find, -} from "lodash/fp" -import { 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 = compileCode(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/cloneApp.js b/packages/core/src/appInitialise/cloneApp.js deleted file mode 100644 index eb335610b3..0000000000 --- a/packages/core/src/appInitialise/cloneApp.js +++ /dev/null @@ -1,8 +0,0 @@ -import { setCleanupFunc } from "../transactions/setCleanupFunc" - -export const cloneApp = (app, mergeWith) => { - const newApp = { ...app } - Object.assign(newApp, mergeWith) - setCleanupFunc(newApp) - return newApp -} diff --git a/packages/core/src/appInitialise/eventAggregator.js b/packages/core/src/appInitialise/eventAggregator.js deleted file mode 100644 index 152d5a4677..0000000000 --- a/packages/core/src/appInitialise/eventAggregator.js +++ /dev/null @@ -1,27 +0,0 @@ -import { has } from "lodash/fp" - -const publish = handlers => async (eventName, context = {}) => { - if (!has(eventName)(handlers)) return - - for (const handler of handlers[eventName]) { - await handler(eventName, context) - } -} - -const subscribe = handlers => (eventName, handler) => { - if (!has(eventName)(handlers)) { - 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/index.js b/packages/core/src/appInitialise/index.js deleted file mode 100644 index 137bf0ee8a..0000000000 --- a/packages/core/src/appInitialise/index.js +++ /dev/null @@ -1,13 +0,0 @@ -export const setupDatastore = datastore => { - datastore.loadJson = datastore.loadFile - datastore.createJson = datastore.createFile - datastore.updateJson = datastore.updateFile - 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 deleted file mode 100644 index 718056a403..0000000000 --- a/packages/core/src/appInitialise/initialiseData.js +++ /dev/null @@ -1,55 +0,0 @@ -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 { _getNew } from "../recordApi/getNew" -import { _save } from "../recordApi/save" - -export const initialiseData = async ( - datastore, - applicationDefinition, - accessLevels -) => { - if (!(await datastore.exists(appDefinitionFile))) - await datastore.createJson(appDefinitionFile, applicationDefinition) - - if (!(await datastore.exists(USERS_LIST_FILE))) - await datastore.createJson(USERS_LIST_FILE, []) - - if (!(await datastore.exists(ACCESS_LEVELS_FILE))) - await datastore.createJson( - ACCESS_LEVELS_FILE, - accessLevels ? accessLevels : { version: 0, levels: [] } - ) - - await initialiseRootSingleRecords(datastore, applicationDefinition.hierarchy) -} - -const initialiseRootSingleRecords = async (datastore, hierarchy) => { - const app = { - publish: () => {}, - cleanupTransactions: () => {}, - datastore, - hierarchy, - } - - const flathierarchy = getFlattenedHierarchy(hierarchy) - const singleRecords = $(flathierarchy, [filter(isSingleRecord)]) - - for (let record of singleRecords) { - const result = _getNew(record, "") - result.key = record.nodeKey() - await _save(app, result) - } -} diff --git a/packages/core/src/authApi/authCommon.js b/packages/core/src/authApi/authCommon.js deleted file mode 100644 index 2420a2f8a6..0000000000 --- a/packages/core/src/authApi/authCommon.js +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 8fdcc80d38..0000000000 --- a/packages/core/src/authApi/authenticate.js +++ /dev/null @@ -1,118 +0,0 @@ -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 = 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 = 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 deleted file mode 100644 index 204dc44653..0000000000 --- a/packages/core/src/authApi/createTemporaryAccess.js +++ /dev/null @@ -1,70 +0,0 @@ -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: 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 deleted file mode 100644 index fa953f9c9f..0000000000 --- a/packages/core/src/authApi/createUser.js +++ /dev/null @@ -1,96 +0,0 @@ -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.map(e => e.error))}`) - } - - 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 = 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 deleted file mode 100644 index 07e4b9c29b..0000000000 --- a/packages/core/src/authApi/enableUser.js +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 74b16317b9..0000000000 --- a/packages/core/src/authApi/generateFullPermissions.js +++ /dev/null @@ -1,36 +0,0 @@ -import { filter, values, each, keys } from "lodash/fp" -import { permission } from "./permissions" -import { - getFlattenedHierarchy, - isIndex, - isModel, -} from "../templateApi/hierarchy" -import { $ } from "../common" - -export const generateFullPermissions = app => { - const allNodes = getFlattenedHierarchy(app.hierarchy) - const accessLevel = { permissions: [] } - - const recordNodes = $(allNodes, [filter(isModel)]) - - 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 deleted file mode 100644 index 5a5713f2ba..0000000000 --- a/packages/core/src/authApi/getNewAccessLevel.js +++ /dev/null @@ -1,5 +0,0 @@ -export const getNewAccessLevel = () => () => ({ - name: "", - permissions: [], - default: false, -}) diff --git a/packages/core/src/authApi/getNewUser.js b/packages/core/src/authApi/getNewUser.js deleted file mode 100644 index c4da445a37..0000000000 --- a/packages/core/src/authApi/getNewUser.js +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index 3915076b02..0000000000 --- a/packages/core/src/authApi/getUsers.js +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 046e3bc456..0000000000 --- a/packages/core/src/authApi/index.js +++ /dev/null @@ -1,46 +0,0 @@ -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 deleted file mode 100644 index c1cf96f7ff..0000000000 --- a/packages/core/src/authApi/isAuthorized.js +++ /dev/null @@ -1,44 +0,0 @@ -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 deleted file mode 100644 index 52ee224940..0000000000 --- a/packages/core/src/authApi/loadAccessLevels.js +++ /dev/null @@ -1,16 +0,0 @@ -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 deleted file mode 100644 index 3dc2a14ade..0000000000 --- a/packages/core/src/authApi/permissions.js +++ /dev/null @@ -1,81 +0,0 @@ -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 deleted file mode 100644 index 0e9f04408d..0000000000 --- a/packages/core/src/authApi/saveAccessLevels.js +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100644 index c708cf3279..0000000000 --- a/packages/core/src/authApi/setPassword.js +++ /dev/null @@ -1,160 +0,0 @@ -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 = 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 = 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 = 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 deleted file mode 100644 index 901b02768f..0000000000 --- a/packages/core/src/authApi/setUserAccessLevels.js +++ /dev/null @@ -1,60 +0,0 @@ -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 deleted file mode 100644 index bd22cd73b0..0000000000 --- a/packages/core/src/authApi/validateAccessLevels.js +++ /dev/null @@ -1,93 +0,0 @@ -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 isModelOrIndexType = 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 => - !isModelOrIndexType(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 deleted file mode 100644 index 6d6afaa69d..0000000000 --- a/packages/core/src/authApi/validateUser.js +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index 897c199c46..0000000000 --- a/packages/core/src/collectionApi/delete.js +++ /dev/null @@ -1,57 +0,0 @@ -import { safeKey, apiWrapper, events, joinKey } from "../common" -import { _deleteRecord } from "../recordApi/delete" -import { getAllIdsIterator } from "../indexing/allIds" -import { permission } from "../authApi/permissions" -import { getCollectionDir } from "../recordApi/recordInfo" -import { ensureCollectionIsInitialised } from "./initialise" -import { getNodeForCollectionPath } from "../templateApi/hierarchy" - -export const deleteCollection = (app, disableCleanup = false) => async key => - apiWrapper( - app, - events.collectionApi.delete, - permission.manageCollection.isAuthorized, - { key }, - _deleteCollection, - app, - key, - disableCleanup - ) - -/* - const recordNode = getCollectionNode(app.hierarchy, key); - -*/ - -export const _deleteCollection = async (app, key, disableCleanup) => { - key = safeKey(key) - const collectionDir = getCollectionDir(app.hierarchy, key) - await deleteRecords(app, key) - await deleteCollectionFolder(app, key, collectionDir) - if (!disableCleanup) { - await app.cleanupTransactions() - } -} - -const deleteCollectionFolder = async (app, key, dir) => { - await app.datastore.deleteFolder(dir) - await ensureCollectionIsInitialised( - app.datastore, - getNodeForCollectionPath(app.hierarchy)(key), - dir) -} - -const deleteRecords = async (app, key) => { - 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) - } - } - - ids = await iterate() - } -} diff --git a/packages/core/src/collectionApi/getAllowedRecordTypes.js b/packages/core/src/collectionApi/getAllowedRecordTypes.js deleted file mode 100644 index b14e1a1de5..0000000000 --- a/packages/core/src/collectionApi/getAllowedRecordTypes.js +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index e9d60a73f4..0000000000 --- a/packages/core/src/collectionApi/index.js +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 3aac1c2889..0000000000 --- a/packages/core/src/collectionApi/initialise.js +++ /dev/null @@ -1,48 +0,0 @@ -import { filter } from "lodash/fp" -import { - getFlattenedHierarchy, - isCollectionRecord, - isRoot, -} from "../templateApi/hierarchy" -import { $, allTrue, joinKey } from "../common" - -export const ensureCollectionIsInitialised = async (datastore, node, dir) => { - if (!(await datastore.exists(dir))) { - await datastore.createFolder(dir) - await datastore.createFolder(joinKey(dir, node.nodeId)) - } -} - -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, recordInfo) => { - const childCollectionRecords = $(recordInfo.recordNode, [ - n => n.children, - filter(isCollectionRecord), - ]) - - for (const child of childCollectionRecords) { - await ensureCollectionIsInitialised( - app.datastore, - child, - recordInfo.child(child.collectionName) - ) - } -} diff --git a/packages/core/src/common/apiWrapper.js b/packages/core/src/common/apiWrapper.js deleted file mode 100644 index 54a9a36acc..0000000000 --- a/packages/core/src/common/apiWrapper.js +++ /dev/null @@ -1,128 +0,0 @@ -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 deleted file mode 100644 index 5e442c89a5..0000000000 --- a/packages/core/src/common/compileCode.js +++ /dev/null @@ -1,26 +0,0 @@ -import { compileCode as cCode } from "@nx-js/compiler-util" -import { includes } from "lodash/fp" - -export const compileCode = code => { - let func - let safeCode - - if (includes("return ")(code)) { - safeCode = code - } else { - let trimmed = code.trim() - trimmed = trimmed.endsWith(";") - ? trimmed.substring(0, trimmed.length - 1) - : trimmed - safeCode = `return (${trimmed})` - } - - try { - func = cCode(safeCode) - } catch (e) { - e.message = `Error compiling code : ${code} : ${e.message}` - throw e - } - - return func -} diff --git a/packages/core/src/common/errors.js b/packages/core/src/common/errors.js deleted file mode 100644 index 455b334c71..0000000000 --- a/packages/core/src/common/errors.js +++ /dev/null @@ -1,34 +0,0 @@ -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 - } -} diff --git a/packages/core/src/common/events.js b/packages/core/src/common/events.js deleted file mode 100644 index 4c890926a8..0000000000 --- a/packages/core/src/common/events.js +++ /dev/null @@ -1,74 +0,0 @@ -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(), - }, - 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 deleted file mode 100644 index 484a1532c4..0000000000 --- a/packages/core/src/common/index.js +++ /dev/null @@ -1,317 +0,0 @@ -import { - head, - tail, - findIndex, - startsWith, - dropRight, - flow, - takeRight, - trim, - replace, -} from "lodash" -import { - some, - reduce, - isEmpty, - isArray, - join, - isString, - isInteger, - isDate, - toNumber, - isUndefined, - isNaN, - isNull, - constant, - split, - includes, - filter, -} from "lodash/fp" -import { events, eventsList } from "./events" -import { apiWrapper } from "./apiWrapper" -import { getLock, NO_LOCK, isNolock } from "./lock" -import crypto from "./nodeCrypto" - -// 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(keySep)(str) -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 $(paramsOrArray, [ - filter(s => !isUndefined(s) && !isNull(s) && s.toString().length > 0), - join(keySep), - safeKey, - ]) -} -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( - (result, conditionFunc) => - (isNull(result) || result == true) && conditionFunc(val), - null - )(funcArgs) - -export const anyTrue = (...funcArgs) => val => - reduce( - (result, conditionFunc) => result == true || conditionFunc(val), - null - )(funcArgs) - -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(val)(vals) -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 pushAll = (target, items) => { - for (let i of items) target.push(i) -} - -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 { crypto } - -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, - pushAll, -} diff --git a/packages/core/src/common/lock.js b/packages/core/src/common/lock.js deleted file mode 100644 index 32eef21630..0000000000 --- a/packages/core/src/common/lock.js +++ /dev/null @@ -1,109 +0,0 @@ -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/nodeCrypto.js b/packages/core/src/common/nodeCrypto.js deleted file mode 100644 index 755e3d938e..0000000000 --- a/packages/core/src/common/nodeCrypto.js +++ /dev/null @@ -1,14 +0,0 @@ -import bcrypt from "bcryptjs" - -function hash(password) { - return bcrypt.hashSync(password, 10) -} - -function verify(hash, password) { - return bcrypt.compareSync(password, hash) -} - -export default { - hash, - verify, -} diff --git a/packages/core/src/common/validationCommon.js b/packages/core/src/common/validationCommon.js deleted file mode 100644 index 51029e1fc6..0000000000 --- a/packages/core/src/common/validationCommon.js +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index 6c77eb337d..0000000000 --- a/packages/core/src/index.js +++ /dev/null @@ -1,112 +0,0 @@ -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, crypto } from "./common" -import { setCleanupFunc } from "./transactions/setCleanupFunc" -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) - - setCleanupFunc(app, cleanupTransactions) - - 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 = () => userWithFullAccess(app) - - 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 const userWithFullAccess = app => { - app.user = { - name: "app", - permissions: generateFullPermissions(app), - isUser: false, - temp: false, - } - return app.user -} - -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 { hierarchy } -export { common } -export { crypto } - -export default getAppApis diff --git a/packages/core/src/indexApi/aggregates.js b/packages/core/src/indexApi/aggregates.js deleted file mode 100644 index 178f37f45a..0000000000 --- a/packages/core/src/indexApi/aggregates.js +++ /dev/null @@ -1,180 +0,0 @@ -import { has, isNumber, isUndefined } from "lodash/fp" -import { compileCode } from "../common/compileCode" -import { safeKey, apiWrapper, events, isNonEmptyString } from "../common" -import { iterateIndex } from "../indexing/read" -import { - getUnshardedIndexDataKey, - getShardKeysInRange, -} from "../indexing/sharding" -import { - getExactNodeForKey, - isIndex, - isShardedIndex, -} from "../templateApi/hierarchy" -import { CONTINUE_READING_RECORDS } from "../indexing/serializer" -import { permission } from "../authApi/permissions" -import { BadRequestError } from "../common/errors" -import { getIndexDir } from "./getIndexDir" - -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 = getExactNodeForKey(app.hierarchy)(indexKey) - const indexDir = getIndexDir(app.hierarchy, indexKey) - - if (!isIndex(indexNode)) { - throw new BadRequestError("supplied key is not an index") - } - - if (isShardedIndex(indexNode)) { - const shardKeys = await getShardKeysInRange( - app, - indexNode, - indexDir, - 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(indexDir) - ) -} - -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 (!compileCode(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 deleted file mode 100644 index 76a5ba1826..0000000000 --- a/packages/core/src/indexApi/buildIndex.js +++ /dev/null @@ -1,143 +0,0 @@ -import { filter, includes, some } from "lodash/fp" -import { getAllIdsIterator } from "../indexing/allIds" -import { - getFlattenedHierarchy, - getRecordNodeById, - getNode, - isIndex, - isModel, - getActualKeyOfParent, - getAllowedRecordNodesForIndex, - fieldReversesReferenceToIndex, - isTopLevelIndex, -} from "../templateApi/hierarchy" -import { joinKey, apiWrapper, events, $ } from "../common" -import { - createBuildIndexFolder, - transactionForBuildIndex, -} from "../transactions/create" -import { permission } from "../authApi/permissions" -import { BadRequestError } from "../common/errors" -import { initialiseIndex } from "../indexing/initialiseIndex" -import { getRecordInfo } from "../recordApi/recordInfo" - -/** 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 - ) - -export 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 => - isModel(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 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 recordNodeApplies = indexNode => recordNode => - includes(recordNode.nodeId)(indexNode.allowedModelNodeIds) - -export default buildIndex diff --git a/packages/core/src/indexApi/delete.js b/packages/core/src/indexApi/delete.js deleted file mode 100644 index bd8e1997b9..0000000000 --- a/packages/core/src/indexApi/delete.js +++ /dev/null @@ -1,38 +0,0 @@ -import { tryAwaitOrIgnore, safeKey } from "../common" -import { - isIndex, - isShardedIndex, - getExactNodeForKey, -} from "../templateApi/hierarchy" -import { - getAllShardKeys, - getShardMapKey, - getUnshardedIndexDataKey, -} from "../indexing/sharding" -import { getIndexDir } from "./getIndexDir" - -export const _deleteIndex = async (app, indexKey, includeFolder) => { - indexKey = safeKey(indexKey) - const indexNode = getExactNodeForKey(app.hierarchy)(indexKey) - const indexDir = getIndexDir(app.hierarchy, indexKey) - - if (!isIndex(indexNode)) { - throw new Error("Supplied key is not an index") - } - - if (isShardedIndex(indexNode)) { - const shardKeys = await getAllShardKeys(app, indexNode, indexDir) - for (const k of shardKeys) { - await tryAwaitOrIgnore(app.datastore.deleteFile(k)) - } - tryAwaitOrIgnore(await app.datastore.deleteFile(getShardMapKey(indexDir))) - } else { - await tryAwaitOrIgnore( - app.datastore.deleteFile(getUnshardedIndexDataKey(indexDir)) - ) - } - - if (includeFolder) { - tryAwaitOrIgnore(await app.datastore.deleteFolder(indexDir)) - } -} diff --git a/packages/core/src/indexApi/getIndexDir.js b/packages/core/src/indexApi/getIndexDir.js deleted file mode 100644 index 0afd1edcf6..0000000000 --- a/packages/core/src/indexApi/getIndexDir.js +++ /dev/null @@ -1,14 +0,0 @@ -import { getRecordInfo } from "../recordApi/recordInfo" -import { getParentKey, getLastPartInKey } from "../templateApi/hierarchy" -import { keySep } from "../common" - -export const getIndexDir = (hierarchy, indexKey) => { - const parentKey = getParentKey(indexKey) - - if (parentKey === "") return indexKey - if (parentKey === keySep) return indexKey - - const recordInfo = getRecordInfo(hierarchy, parentKey) - - return recordInfo.child(getLastPartInKey(indexKey)) -} diff --git a/packages/core/src/indexApi/index.js b/packages/core/src/indexApi/index.js deleted file mode 100644 index aacf497c09..0000000000 --- a/packages/core/src/indexApi/index.js +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 5bafa1b840..0000000000 --- a/packages/core/src/indexApi/listItems.js +++ /dev/null @@ -1,76 +0,0 @@ -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 { - getExactNodeForKey, - isIndex, - isShardedIndex, -} from "../templateApi/hierarchy" -import { permission } from "../authApi/permissions" -import { getIndexDir } from "./getIndexDir" - -export const listItems = app => async (indexKey, options) => { - indexKey = safeKey(indexKey) - return apiWrapper( - app, - events.indexApi.listItems, - permission.readIndex.isAuthorized(indexKey), - { indexKey, options }, - _listItems, - app, - indexKey, - options - ) -} - -const defaultOptions = { - rangeStartParams: null, - rangeEndParams: null, - searchPhrase: null, -} - -export const _listItems = async (app, indexKey, options = defaultOptions) => { - const { searchPhrase, rangeStartParams, rangeEndParams } = $({}, [ - merge(options), - merge(defaultOptions), - ]) - - const getItems = async indexedDataKey => - isNonEmptyString(searchPhrase) - ? await searchIndex( - app.hierarchy, - app.datastore, - indexNode, - indexedDataKey, - searchPhrase - ) - : await readIndex(app.hierarchy, app.datastore, indexNode, indexedDataKey) - - indexKey = safeKey(indexKey) - const indexNode = getExactNodeForKey(app.hierarchy)(indexKey) - const indexDir = getIndexDir(app.hierarchy, indexKey) - - if (!isIndex(indexNode)) { - throw new Error("supplied key is not an index") - } - - if (isShardedIndex(indexNode)) { - const shardKeys = await getShardKeysInRange( - app, - indexNode, - indexDir, - rangeStartParams, - rangeEndParams - ) - const items = [] - for (const k of shardKeys) { - items.push(await getItems(k)) - } - return flatten(items) - } - return await getItems(getUnshardedIndexDataKey(indexDir)) -} diff --git a/packages/core/src/indexing/allIds.js b/packages/core/src/indexing/allIds.js deleted file mode 100644 index 465ab4a1ad..0000000000 --- a/packages/core/src/indexing/allIds.js +++ /dev/null @@ -1,279 +0,0 @@ -import { flatten, orderBy, filter, isUndefined } from "lodash/fp" -import { - getFlattenedHierarchy, - getCollectionNodeByKeyOrNodeKey, - getNodeByKeyOrNodeKey, - isCollectionRecord, - isAncestor, -} from "../templateApi/hierarchy" -import { joinKey, safeKey, $ } from "../common" -import { getCollectionDir } from "../recordApi/recordInfo" - -export const RECORDS_PER_FOLDER = 1000 -export const allIdChars = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" - -// this should never be changed - ever -// - existing databases depend on the order of chars this string - -/** - * folderStructureArray should return an array like - * - [1] = all records fit into one folder - * - [2] = all records fite into 2 folders - * - [64, 3] = all records fit into 64 * 3 folders - * - [64, 64, 10] = all records fit into 64 * 64 * 10 folder - * (there are 64 possible chars in allIsChars) - */ -export const folderStructureArray = recordNode => { - const totalFolders = Math.ceil(recordNode.estimatedRecordCount / 1000) - const folderArray = [] - let levelCount = 1 - while (64 ** levelCount < totalFolders) { - levelCount += 1 - folderArray.push(64) - } - - const parentFactor = 64 ** folderArray.length - if (parentFactor < totalFolders) { - folderArray.push(Math.ceil(totalFolders / parentFactor)) - } - - return folderArray - - /* - const maxRecords = currentFolderPosition === 0 - ? RECORDS_PER_FOLDER - : currentFolderPosition * 64 * RECORDS_PER_FOLDER; - - if(maxRecords < recordNode.estimatedRecordCount) { - return folderStructureArray( - recordNode, - [...currentArray, 64], - currentFolderPosition + 1); - } else { - const childFolderCount = Math.ceil(recordNode.estimatedRecordCount / maxRecords ); - return [...currentArray, childFolderCount] - }*/ -} - -export const getAllIdsIterator = app => async collection_Key_or_NodeKey => { - collection_Key_or_NodeKey = safeKey(collection_Key_or_NodeKey) - const recordNode = - getCollectionNodeByKeyOrNodeKey(app.hierarchy, collection_Key_or_NodeKey) || - getNodeByKeyOrNodeKey(app.hierarchy, collection_Key_or_NodeKey) - - const getAllIdsIteratorForCollectionKey = async ( - recordNode, - collectionKey - ) => { - const folderStructure = folderStructureArray(recordNode) - - let currentFolderContents = [] - let currentPosition = [] - - const collectionDir = getCollectionDir(app.hierarchy, collectionKey) - const basePath = joinKey(collectionDir, recordNode.nodeId.toString()) - - // "folderStructure" determines the top, sharding folders - // we need to add one, for the collection root folder, which - // always exists - const levels = folderStructure.length + 1 - const topLevel = levels - 1 - - /* populate initial directory structure in form: - [ - {path: "/a", contents: ["b", "c", "d"]}, - {path: "/a/b", contents: ["e","f","g"]}, - {path: "/a/b/e", contents: ["1-abcd","2-cdef","3-efgh"]}, - ] - // stores contents on each parent level - // top level has ID folders - */ - const firstFolder = async () => { - let folderLevel = 0 - - const lastPathHasContent = () => - folderLevel === 0 || - currentFolderContents[folderLevel - 1].contents.length > 0 - - while (folderLevel <= topLevel && lastPathHasContent()) { - let thisPath = basePath - for (let lev = 0; lev < currentPosition.length; lev++) { - thisPath = joinKey(thisPath, currentFolderContents[lev].contents[0]) - } - - const contentsThisLevel = await app.datastore.getFolderContents( - thisPath - ) - currentFolderContents.push({ - contents: contentsThisLevel, - path: thisPath, - }) - - // should start as something like [0,0] - if (folderLevel < topLevel) currentPosition.push(0) - - folderLevel += 1 - } - - return currentPosition.length === levels - 1 - } - - const isOnLastFolder = level => { - const result = - currentPosition[level] === - currentFolderContents[level].contents.length - 1 - return result - } - - const getNextFolder = async (lev = undefined) => { - lev = isUndefined(lev) ? topLevel : lev - const parentLev = lev - 1 - - if (parentLev < 0) return false - - if (isOnLastFolder(parentLev)) { - return await getNextFolder(parentLev) - } - - const newPosition = currentPosition[parentLev] + 1 - currentPosition[parentLev] = newPosition - - const nextFolder = joinKey( - currentFolderContents[parentLev].path, - currentFolderContents[parentLev].contents[newPosition] - ) - currentFolderContents[ - lev - ].contents = await app.datastore.getFolderContents(nextFolder) - currentFolderContents[lev].path = nextFolder - - if (lev !== topLevel) { - // we just advanced a parent folder, so now need to - // do the same to the next levels - let loopLevel = lev + 1 - while (loopLevel <= topLevel) { - const loopParentLevel = loopLevel - 1 - - currentPosition[loopParentLevel] = 0 - const nextLoopFolder = joinKey( - currentFolderContents[loopParentLevel].path, - currentFolderContents[loopParentLevel].contents[0] - ) - currentFolderContents[ - loopLevel - ].contents = await app.datastore.getFolderContents(nextLoopFolder) - currentFolderContents[loopLevel].path = nextLoopFolder - loopLevel += 1 - } - } - - // true ==has more ids... (just loaded more) - return true - } - - const idsCurrentFolder = () => - currentFolderContents[currentFolderContents.length - 1].contents - - const fininshedResult = { done: true, result: { ids: [], collectionKey } } - - let hasStarted = false - let hasMore = true - const getIdsFromCurrentfolder = async () => { - if (!hasMore) { - return fininshedResult - } - - if (!hasStarted) { - hasMore = await firstFolder() - hasStarted = true - return { - result: { - ids: idsCurrentFolder(), - collectionKey, - }, - done: false, - } - } - - hasMore = await getNextFolder() - - return { - result: { - ids: hasMore ? idsCurrentFolder() : [], - collectionKey, - }, - done: !hasMore, - } - } - - return getIdsFromCurrentfolder - } - - const ancestors = $(getFlattenedHierarchy(app.hierarchy), [ - filter(isCollectionRecord), - filter( - n => isAncestor(recordNode)(n) || n.nodeKey() === recordNode.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() === recordNode.nodeKey()) { - return [ - await getAllIdsIteratorForCollectionKey( - currentNode, - currentCollectionKey - ), - ] - } - const allIterators = [] - const currentIterator = await getAllIdsIteratorForCollectionKey( - currentNode, - 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 } - } -} - -export default getAllIdsIterator diff --git a/packages/core/src/indexing/apply.js b/packages/core/src/indexing/apply.js deleted file mode 100644 index e994a5cc5b..0000000000 --- a/packages/core/src/indexing/apply.js +++ /dev/null @@ -1,107 +0,0 @@ -import { ensureShardNameIsInShardMap } from "./sharding" -import { getIndexWriter } from "./serializer" -import { isShardedIndex, getParentKey } from "../templateApi/hierarchy" -import { promiseWriteableStream } from "./promiseWritableStream" -import { promiseReadableStream } from "./promiseReadableStream" - -export const applyToShard = async ( - hierarchy, - store, - indexDir, - indexNode, - indexShardKey, - recordsToWrite, - keysToRemove -) => { - const createIfNotExists = recordsToWrite.length > 0 - const writer = await getWriter( - hierarchy, - store, - indexDir, - 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, - indexDir, - indexedDataKey, - indexNode, - createIfNotExists -) => { - let readableStream = null - - if (isShardedIndex(indexNode)) { - await ensureShardNameIsInShardMap(store, indexDir, indexedDataKey) - if (!(await store.exists(indexedDataKey))) { - if (await store.exists(getParentKey(indexedDataKey))) { - await store.createFile(indexedDataKey, "") - } else { - return SHARD_DELETED - } - } - } - - try { - readableStream = promiseReadableStream( - await store.readableFileStream(indexedDataKey) - ) - } catch (e) { - if (await store.exists(indexedDataKey)) { - throw e - } else { - if (createIfNotExists) { - if (await store.exists(getParentKey(indexedDataKey))) { - await store.createFile(indexedDataKey, "") - } else { - return SHARD_DELETED - } - } 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 - - // if parent folder does not exist, assume that this index - // should not be there - if (!(await store.exists(getParentKey(indexedDataKey)))) { - return - } - } - 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 deleted file mode 100644 index aaa2e6f44a..0000000000 --- a/packages/core/src/indexing/evaluate.js +++ /dev/null @@ -1,85 +0,0 @@ -import { compileCode } from "../common/compileCode" -import { isUndefined, keys, cloneDeep, isFunction, includes } from "lodash/fp" -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 => compileCode(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] - } - if (key === "IsNew") { - delete mapped.IsNew - } - } - - 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 deleted file mode 100644 index 1111b56838..0000000000 --- a/packages/core/src/indexing/indexSchemaCreator.js +++ /dev/null @@ -1,58 +0,0 @@ -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 alpha - 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 deleted file mode 100644 index d48c1ddb54..0000000000 --- a/packages/core/src/indexing/initialiseIndex.js +++ /dev/null @@ -1,27 +0,0 @@ -import { isShardedIndex } from "../templateApi/hierarchy" -import { joinKey } from "../common" -import { - getShardMapKey, - getUnshardedIndexDataKey, - createIndexFile, -} from "./sharding" - -export const initialiseIndex = async (datastore, dir, index) => { - const indexDir = joinKey(dir, index.name) - - let newDir = false - if (!(await datastore.exists(indexDir))) { - await datastore.createFolder(indexDir) - newDir = true - } - - if (isShardedIndex(index)) { - const shardFile = getShardMapKey(indexDir) - if (newDir || !(await datastore.exists(shardFile))) - await datastore.createFile(shardFile, "[]") - } else { - const indexFile = getUnshardedIndexDataKey(indexDir) - if (newDir || !(await datastore.exists(indexFile))) - await createIndexFile(datastore, indexFile, index) - } -} diff --git a/packages/core/src/indexing/promiseReadableStream.js b/packages/core/src/indexing/promiseReadableStream.js deleted file mode 100644 index 98063b0227..0000000000 --- a/packages/core/src/indexing/promiseReadableStream.js +++ /dev/null @@ -1,80 +0,0 @@ -// 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 diff --git a/packages/core/src/indexing/promiseWritableStream.js b/packages/core/src/indexing/promiseWritableStream.js deleted file mode 100644 index b4cbf7278f..0000000000 --- a/packages/core/src/indexing/promiseWritableStream.js +++ /dev/null @@ -1,117 +0,0 @@ -// 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 diff --git a/packages/core/src/indexing/read.js b/packages/core/src/indexing/read.js deleted file mode 100644 index 21e68c1bda..0000000000 --- a/packages/core/src/indexing/read.js +++ /dev/null @@ -1,93 +0,0 @@ -import lunr from "lunr" -import { promiseReadableStream } from "./promiseReadableStream" -import { createIndexFile } from "./sharding" -import { generateSchema } from "./indexSchemaCreator" -import { getIndexReader, CONTINUE_READING_RECORDS } from "./serializer" -import { - getAllowedRecordNodesForIndex, - getRecordNodeId, -} from "../templateApi/hierarchy" -import { $ } from "../common" -import { filter, includes, find } from "lodash/fp" - -export const readIndex = async ( - hierarchy, - datastore, - index, - indexedDataKey -) => { - const records = [] - const getType = typeLoader(index, hierarchy) - const doRead = iterateIndex( - async item => { - item.type = getType(item.key) - 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 getType = typeLoader(index, hierarchy) - const doRead = iterateIndex( - async item => { - item.type = getType(item.key) - 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 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 [] - } -} - -const typeLoader = (index, hierarchy) => { - const allowedNodes = getAllowedRecordNodesForIndex(hierarchy, index) - return key => find(n => getRecordNodeId(key) === n.nodeId)(allowedNodes).name -} diff --git a/packages/core/src/indexing/relevant.js b/packages/core/src/indexing/relevant.js deleted file mode 100644 index 850869920f..0000000000 --- a/packages/core/src/indexing/relevant.js +++ /dev/null @@ -1,126 +0,0 @@ -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, - getExactNodeForKey, - recordNodeIdIsAllowed, - isModel, - isGlobalIndex, -} from "../templateApi/hierarchy" -import { indexTypes } from "../templateApi/indexes" -import { getIndexDir } from "../indexApi/getIndexDir" -import { getRecordInfo } from "../recordApi/recordInfo" - -export const getRelevantAncestorIndexes = (hierarchy, record) => { - const key = record.key - const keyParts = splitKey(key) - const nodeId = getRecordNodeId(key) - - const flatHierarchy = orderBy( - getFlattenedHierarchy(hierarchy), - [node => node.pathRegx().length], - ["desc"] - ) - - const makeindexNodeAndDir_ForAncestorIndex = (indexNode, parentRecordDir) => - makeIndexNodeAndDir(indexNode, joinKey(parentRecordDir, 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 (!isModel(nodeMatch) || nodeMatch.indexes.length === 0) { - return acc - } - - const indexes = $(nodeMatch.indexes, [ - filter( - i => - i.indexType === indexTypes.ancestor && - (i.allowedModelNodeIds.length === 0 || - includes(nodeId)(i.allowedModelNodeIds)) - ), - ]) - - const currentRecordDir = getRecordInfo(hierarchy, currentIndexKey).dir - - each(v => - acc.nodesAndKeys.push( - makeindexNodeAndDir_ForAncestorIndex(v, currentRecordDir) - ) - )(indexes) - - return acc - }, - { lastIndexKey: "", nodesAndKeys: [] } - )(keyParts).nodesAndKeys - - const rootIndexes = $(flatHierarchy, [ - filter(n => isGlobalIndex(n) && recordNodeIdIsAllowed(n)(nodeId)), - map(i => makeIndexNodeAndDir(i, getIndexDir(hierarchy, i.nodeKey()))), - ]) - - return union(traverseAncestorIndexesInPath())(rootIndexes) -} - -export const getRelevantReverseReferenceIndexes = (hierarchy, record) => - $(record.key, [ - getExactNodeForKey(hierarchy), - 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(hierarchy, n), - field: f, - })), - ]) - ), - flatten, - map(n => - makeIndexNodeAndDir( - n.recordNode, - joinKey( - getRecordInfo(hierarchy, record[n.field.name].key).dir, - n.recordNode.name - ) - ) - ), - ]) - -const makeIndexNodeAndDir = (indexNode, indexDir) => ({ indexNode, indexDir }) - -export default getRelevantAncestorIndexes diff --git a/packages/core/src/indexing/serializer.js b/packages/core/src/indexing/serializer.js deleted file mode 100644 index a735f1e0dd..0000000000 --- a/packages/core/src/indexing/serializer.js +++ /dev/null @@ -1,238 +0,0 @@ -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 -} diff --git a/packages/core/src/indexing/sharding.js b/packages/core/src/indexing/sharding.js deleted file mode 100644 index 0b8eaaf97e..0000000000 --- a/packages/core/src/indexing/sharding.js +++ /dev/null @@ -1,116 +0,0 @@ -import { compileCode } from "../common/compileCode" -import { filter, includes, map, last } from "lodash/fp" -import { - getActualKeyOfParent, - isGlobalIndex, - getParentKey, - isShardedIndex, -} from "../templateApi/hierarchy" -import { joinKey, isNonEmptyString, splitKey, $ } from "../common" - -export const getIndexedDataKey = (indexNode, indexDir, 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(indexDir, shardName) -} - -export const getShardKeysInRange = async ( - app, - indexNode, - indexDir, - startRecord = null, - endRecord = null -) => { - const startShardName = !startRecord - ? null - : shardNameFromKey(getIndexedDataKey(indexNode, indexDir, startRecord)) - - const endShardName = !endRecord - ? null - : shardNameFromKey(getIndexedDataKey(indexNode, indexDir, endRecord)) - - return $(await getShardMap(app.datastore, indexDir), [ - filter( - k => - (startRecord === null || k >= startShardName) && - (endRecord === null || k <= endShardName) - ), - map(k => joinKey(indexDir, `${k}.csv`)), - ]) -} - -export const ensureShardNameIsInShardMap = async ( - store, - indexDir, - indexedDataKey -) => { - const map = await getShardMap(store, indexDir) - const shardName = shardNameFromKey(indexedDataKey) - if (!includes(shardName)(map)) { - map.push(shardName) - await writeShardMap(store, indexDir, map) - } -} - -export const getShardMap = async (datastore, indexDir) => { - const shardMapKey = getShardMapKey(indexDir) - try { - return await datastore.loadJson(shardMapKey) - } catch (_) { - await datastore.createJson(shardMapKey, []) - return [] - } -} - -export const writeShardMap = async (datastore, indexDir, shardMap) => - await datastore.updateJson(getShardMapKey(indexDir), shardMap) - -export const getAllShardKeys = async (app, indexNode, indexDir) => - await getShardKeysInRange(app, indexNode, indexDir) - -export const getShardMapKey = indexDir => joinKey(indexDir, "shardMap.json") - -export const getUnshardedIndexDataKey = indexDir => - joinKey(indexDir, "index.csv") - -export const createIndexFile = async (datastore, indexedDataKey, index) => { - if (isShardedIndex(index)) { - const indexDir = getParentKey(indexedDataKey) - const shardMap = await getShardMap(datastore, indexDir) - shardMap.push(shardNameFromKey(indexedDataKey)) - await writeShardMap(datastore, indexDir, 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/recordApi/customId.js b/packages/core/src/recordApi/customId.js deleted file mode 100644 index 38e9b607ba..0000000000 --- a/packages/core/src/recordApi/customId.js +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index afbb4bd015..0000000000 --- a/packages/core/src/recordApi/delete.js +++ /dev/null @@ -1,35 +0,0 @@ -import { safeKey, apiWrapper, events, joinKey } from "../common" -import { _load } from "./load" -import { _deleteCollection } from "../collectionApi/delete" -import { getExactNodeForKey } from "../templateApi/hierarchy" -import { transactionForDeleteRecord } from "../transactions/create" -import { permission } from "../authApi/permissions" -import { getRecordInfo } from "./recordInfo" - -export const deleteRecord = (app, disableCleanup = false) => async key => { - key = safeKey(key) - return 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) => { - const recordInfo = getRecordInfo(app.hierarchy, key) - key = recordInfo.key - const node = getExactNodeForKey(app.hierarchy)(key) - - for (const collectionRecord of node.children) { - const collectionKey = joinKey(key, collectionRecord.collectionName) - await _deleteCollection(app, collectionKey, true) - } - - await app.datastore.deleteFile(key) -} diff --git a/packages/core/src/recordApi/downloadFile.js b/packages/core/src/recordApi/downloadFile.js deleted file mode 100644 index d38f98678c..0000000000 --- a/packages/core/src/recordApi/downloadFile.js +++ /dev/null @@ -1,31 +0,0 @@ -import { apiWrapper, events, isNothing } from "../common" -import { permission } from "../authApi/permissions" -import { safeGetFullFilePath } from "./uploadFile" -import { BadRequestError } from "../common/errors" -import { getRecordInfo } from "./recordInfo" - -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") - } - - const { dir } = getRecordInfo(app.hierarchy, recordKey) - return await app.datastore.readableFileStream( - safeGetFullFilePath(dir, relativePath) - ) -} diff --git a/packages/core/src/recordApi/getContext.js b/packages/core/src/recordApi/getContext.js deleted file mode 100644 index 27bfd46f74..0000000000 --- a/packages/core/src/recordApi/getContext.js +++ /dev/null @@ -1,76 +0,0 @@ -import { map, isString, has, some } from "lodash/fp" -import { - getExactNodeForKey, - findField, - getNode, - isGlobalIndex, -} from "../templateApi/hierarchy" -import { listItems } from "../indexApi/listItems" -import { $, apiWrapperSync, events, safeKey } from "../common" -import { getIndexKey_BasedOnDecendant } from "../indexing/sharding" -import { permission } from "../authApi/permissions" - -export const getContext = app => recordKey => { - recordKey = safeKey(recordKey) - return apiWrapperSync( - app, - events.recordApi.getContext, - permission.readRecord.isAuthorized(recordKey), - { recordKey }, - _getContext, - app, - recordKey - ) -} - -export const _getContext = (app, recordKey) => { - recordKey = safeKey(recordKey) - const recordNode = getExactNodeForKey(app.hierarchy)(recordKey) - - const cachedReferenceIndexes = {} - - const lazyLoadReferenceIndex = async typeOptions => { - if (!has(typeOptions.indexNodeKey)(cachedReferenceIndexes)) { - 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(i => i.key === key)(data) - }, - 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 deleted file mode 100644 index 931666369f..0000000000 --- a/packages/core/src/recordApi/getNew.js +++ /dev/null @@ -1,51 +0,0 @@ -import { keyBy, mapValues } from "lodash/fp" -import { generate } from "shortid" -import { - getNodeForCollectionPath, - isSingleRecord, -} 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) - collectionKey = safeKey(collectionKey) - return apiWrapperSync( - app, - events.recordApi.getNew, - permission.createRecord.isAuthorized(recordNode.nodeKey()), - { collectionKey, recordTypeName }, - _getNew, - recordNode, - collectionKey - ) -} - -/** - * Constructs a record object that can be saved to the backend. - * @param {*} recordNode - record - * @param {*} collectionKey - nested collection key that the record will be saved to. - */ -export 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 = isSingleRecord(recordNode) - ? joinKey(collectionKey, recordNode.name) - : 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 deleted file mode 100644 index e7023aac19..0000000000 --- a/packages/core/src/recordApi/index.js +++ /dev/null @@ -1,27 +0,0 @@ -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/initialiseChildren.js b/packages/core/src/recordApi/initialiseChildren.js deleted file mode 100644 index f65ad6a33f..0000000000 --- a/packages/core/src/recordApi/initialiseChildren.js +++ /dev/null @@ -1,79 +0,0 @@ -import { isString, flatten, map, filter } from "lodash/fp" -import { initialiseChildCollections } from "../collectionApi/initialise" -import { _loadFromInfo } from "./load" -import { $, joinKey } from "../common" -import { - getFlattenedHierarchy, - isModel, - getNode, - isTopLevelRecord, - fieldReversesReferenceToNode, -} from "../templateApi/hierarchy" -import { initialiseIndex } from "../indexing/initialiseIndex" -import { getRecordInfo } from "./recordInfo" -import { getAllIdsIterator } from "../indexing/allIds" - -export const initialiseChildren = async (app, recordInfoOrKey) => { - const recordInfo = isString(recordInfoOrKey) - ? getRecordInfo(app.hierarchy, recordInfoOrKey) - : recordInfoOrKey - await initialiseReverseReferenceIndexes(app, recordInfo) - await initialiseAncestorIndexes(app, recordInfo) - await initialiseChildCollections(app, recordInfo) -} - -export const initialiseChildrenForNode = async (app, recordNode) => { - if (isTopLevelRecord(recordNode)) { - await initialiseChildren(app, recordNode.nodeKey()) - return - } - - const iterate = await getAllIdsIterator(app)( - recordNode.parent().collectionNodeKey() - ) - let iterateResult = await iterate() - while (!iterateResult.done) { - const { result } = iterateResult - for (const id of result.ids) { - const initialisingRecordKey = joinKey(result.collectionKey, id) - await initialiseChildren(app, initialisingRecordKey) - } - iterateResult = await iterate() - } -} - -const initialiseAncestorIndexes = async (app, recordInfo) => { - for (const index of recordInfo.recordNode.indexes) { - const indexKey = recordInfo.child(index.name) - if (!(await app.datastore.exists(indexKey))) { - await initialiseIndex(app.datastore, recordInfo.dir, index) - } - } -} - -const initialiseReverseReferenceIndexes = async (app, recordInfo) => { - const indexNodes = $( - fieldsThatReferenceThisModel(app, recordInfo.recordNode), - [ - map(f => - $(f.typeOptions.reverseIndexNodeKeys, [ - map(n => getNode(app.hierarchy, n)), - ]) - ), - flatten, - ] - ) - - for (const indexNode of indexNodes) { - await initialiseIndex(app.datastore, recordInfo.dir, indexNode) - } -} - -const fieldsThatReferenceThisModel = (app, recordNode) => - $(app.hierarchy, [ - getFlattenedHierarchy, - filter(isModel), - map(n => n.fields), - flatten, - filter(fieldReversesReferenceToNode(recordNode)), - ]) diff --git a/packages/core/src/recordApi/load.js b/packages/core/src/recordApi/load.js deleted file mode 100644 index b176e4d74b..0000000000 --- a/packages/core/src/recordApi/load.js +++ /dev/null @@ -1,77 +0,0 @@ -import { keyBy, mapValues, filter, map, includes, last } from "lodash/fp" -import { getNode, getExactNodeForKey } 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 _loadFromInfo = () => {}; - -export const getRecordFileName = key => joinKey(key, "record.json") - -export const load = app => async key => { - key = safeKey(key) - return apiWrapper( - app, - events.recordApi.load, - permission.readRecord.isAuthorized(key), - { key }, - _load, - app, - key - ) -} - -export const _load = async (app, key, keyStack = []) => { - const recordNode = getExactNodeForKey(app.hierarchy)(key) - const storedData = await app.datastore.loadJson(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._rev = storedData._rev - loadedRecord._id = storedData._id - loadedRecord.key = key - loadedRecord.id = $(key, [splitKey, last]) - loadedRecord.type = recordNode.name - return loadedRecord -} - -export default load diff --git a/packages/core/src/recordApi/recordInfo.js b/packages/core/src/recordApi/recordInfo.js deleted file mode 100644 index b308d78a95..0000000000 --- a/packages/core/src/recordApi/recordInfo.js +++ /dev/null @@ -1,112 +0,0 @@ -import { - getExactNodeForKey, - getActualKeyOfParent, - isRoot, - isSingleRecord, - getNodeForCollectionPath, -} from "../templateApi/hierarchy" -import { reduce, find, filter } from "lodash/fp" -import { $, getFileFromKey, joinKey, safeKey, keySep } from "../common" -import { folderStructureArray, allIdChars } from "../indexing/allIds" - -export const getRecordInfo = (hierarchy, key) => { - const recordNode = getExactNodeForKey(hierarchy)(key) - const pathInfo = getRecordDirectory(recordNode, key) - const dir = joinKey(pathInfo.base, ...pathInfo.subdirs) - - return { - recordJson: recordJson(dir), - files: files(dir), - child: name => joinKey(dir, name), - key: safeKey(key), - recordNode, - pathInfo, - dir, - } -} - -export const getCollectionDir = (hierarchy, collectionKey) => { - const recordNode = getNodeForCollectionPath(hierarchy)(collectionKey) - const dummyRecordKey = joinKey(collectionKey, "1-abcd") - const pathInfo = getRecordDirectory(recordNode, dummyRecordKey) - return pathInfo.base -} - -const recordJson = dir => joinKey(dir, "record.json") - -const files = dir => joinKey(dir, "files") - -const getRecordDirectory = (recordNode, key) => { - const id = getFileFromKey(key) - - const traverseParentKeys = (n, parents = []) => { - if (isRoot(n)) return parents - const k = getActualKeyOfParent(n.nodeKey(), key) - const thisNodeDir = { - node: n, - relativeDir: joinKey(recordRelativeDirectory(n, getFileFromKey(k))), - } - return traverseParentKeys(n.parent(), [thisNodeDir, ...parents]) - } - - const parentDirs = $(recordNode.parent(), [ - traverseParentKeys, - reduce((key, item) => { - return joinKey(key, item.node.collectionName, item.relativeDir) - }, keySep), - ]) - - const subdirs = isSingleRecord(recordNode) - ? [] - : recordRelativeDirectory(recordNode, id) - const base = isSingleRecord(recordNode) - ? joinKey(parentDirs, recordNode.name) - : joinKey(parentDirs, recordNode.collectionName) - - return { - subdirs, - base, - } -} - -const recordRelativeDirectory = (recordNode, id) => { - const folderStructure = folderStructureArray(recordNode) - const strippedId = id.substring(recordNode.nodeId.toString().length + 1) - const subfolders = $(folderStructure, [ - reduce( - (result, currentCount) => { - result.folders.push( - folderForChar(strippedId[result.level], currentCount) - ) - return { level: result.level + 1, folders: result.folders } - }, - { level: 0, folders: [] } - ), - f => f.folders, - filter(f => !!f), - ]) - - return [recordNode.nodeId.toString(), ...subfolders, id] -} - -const folderForChar = (char, folderCount) => - folderCount === 1 - ? "" - : $(folderCount, [idFoldersForFolderCount, find(f => f.includes(char))]) - -const idFoldersForFolderCount = folderCount => { - const charRangePerShard = 64 / folderCount - const idFolders = [] - let index = 0 - let currentIdsShard = "" - while (index < 64) { - currentIdsShard += allIdChars[index] - if ((index + 1) % charRangePerShard === 0) { - idFolders.push(currentIdsShard) - currentIdsShard = "" - } - index++ - } - - return idFolders -} diff --git a/packages/core/src/recordApi/save.js b/packages/core/src/recordApi/save.js deleted file mode 100644 index 5f9b322f67..0000000000 --- a/packages/core/src/recordApi/save.js +++ /dev/null @@ -1,65 +0,0 @@ -import { cloneDeep } from "lodash/fp" -import { validate } from "./validate" -import { _load } from "./load" -import { apiWrapper, events } from "../common" -import { permission } from "../authApi/permissions" -import { BadRequestError } from "../common/errors" -import { getExactNodeForKey } from "../templateApi/hierarchy" - -export const save = app => async (record, context) => - apiWrapper( - app, - events.recordApi.save, - record._rev - ? permission.updateRecord.isAuthorized(record.key) - : permission.createRecord.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)}` - ) - } - } - - const recordNode = getExactNodeForKey(app.hierarchy)(record.key) - - recordClone.nodeKey = recordNode.nodeKey() - - if (!record._rev) { - if (!recordNode) throw new Error("Cannot find node for " + record.key) - - // FILES - // await app.datastore.createFolder(files) - await app.datastore.createJson(record.key, recordClone) - await app.publish(events.recordApi.save.onRecordCreated, { - record: recordClone, - }) - } else { - const oldRecord = await _load(app, record.key) - await app.datastore.updateJson(record.key, recordClone) - await app.publish(events.recordApi.save.onRecordUpdated, { - old: oldRecord, - new: recordClone, - }) - } - - // TODO: use nano.head to get _rev (saves loading whole doc) - const savedResult = await app.datastore.loadFile(record.key) - recordClone._rev = savedResult._rev - return recordClone -} diff --git a/packages/core/src/recordApi/uploadFile.js b/packages/core/src/recordApi/uploadFile.js deleted file mode 100644 index 613eb35c94..0000000000 --- a/packages/core/src/recordApi/uploadFile.js +++ /dev/null @@ -1,146 +0,0 @@ -import { includes, filter, map, some } from "lodash/fp" -import { generate } from "shortid" -import { _loadFromInfo } from "./load" -import { - apiWrapper, - events, - splitKey, - $, - joinKey, - isNothing, - tryAwaitOrIgnore, -} from "../common" -import { getExactNodeForKey } from "../templateApi/hierarchy" -import { permission } from "../authApi/permissions" -import { isLegalFilename } from "../types/file" -import { BadRequestError, ForbiddenError } from "../common/errors" -import { getRecordInfo } from "./recordInfo" - -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 recordInfo = getRecordInfo(app.hierarchy, recordKey) - const record = await _loadFromInfo(app, recordInfo) - - const fullFilePath = safeGetFullFilePath(recordInfo.dir, 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.` - ) - } - }) - .then(() => tryAwaitOrIgnore(app.datastore.deleteFile, fullFilePath)) - .then(() => app.datastore.renameFile(tempFilePath, fullFilePath)) -} - -const checkFileSizeAgainstFields = ( - app, - record, - relativeFilePath, - expectedSize -) => { - const recordNode = getExactNodeForKey(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 = (recordDir, relativeFilePath) => { - const naughtyUser = () => { - throw new ForbiddenError("naughty naughty") - } - - if (relativeFilePath.startsWith("..")) naughtyUser() - - const pathParts = splitKey(relativeFilePath) - - if (includes("..")(pathParts)) naughtyUser() - - const recordKeyParts = splitKey(recordDir) - - 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 deleted file mode 100644 index 052a92680e..0000000000 --- a/packages/core/src/recordApi/validate.js +++ /dev/null @@ -1,91 +0,0 @@ -import { map, reduce, filter, isEmpty, flatten, each } from "lodash/fp" -import { compileCode } from "../common/compileCode" -import _ from "lodash" -import { getExactNodeForKey } 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 = compileCode(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 = getExactNodeForKey(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/canDeleteIndex.js b/packages/core/src/templateApi/canDeleteIndex.js deleted file mode 100644 index 7857b470c4..0000000000 --- a/packages/core/src/templateApi/canDeleteIndex.js +++ /dev/null @@ -1,54 +0,0 @@ -import { - findRoot, - getFlattenedHierarchy, - fieldReversesReferenceToIndex, - isModel, -} from "./hierarchy" -import { $ } from "../common" -import { map, filter, reduce } from "lodash/fp" - -export const canDeleteIndex = indexNode => { - const flatHierarchy = $(indexNode, [findRoot, getFlattenedHierarchy]) - - const reverseIndexes = $(flatHierarchy, [ - filter(isModel), - reduce((obj, r) => { - for (let field of r.fields) { - if (fieldReversesReferenceToIndex(indexNode)(field)) { - obj.push({ ...field, record: r }) - } - } - return obj - }, []), - map( - f => - `field "${f.name}" on record "${f.record.name}" uses this index as a reference` - ), - ]) - - const lookupIndexes = $(flatHierarchy, [ - filter(isModel), - reduce((obj, r) => { - for (let field of r.fields) { - if ( - field.type === "reference" && - field.typeOptions.indexNodeKey === indexNode.nodeKey() - ) { - obj.push({ ...field, record: r }) - } - } - return obj - }, []), - map( - f => - `field "${f.name}" on record "${f.record.name}" uses this index as a lookup` - ), - ]) - - const errors = [...reverseIndexes, ...lookupIndexes] - - return { - canDelete: errors.length === 0, - errors, - } -} diff --git a/packages/core/src/templateApi/canDeleteModel.js b/packages/core/src/templateApi/canDeleteModel.js deleted file mode 100644 index 811a6501e9..0000000000 --- a/packages/core/src/templateApi/canDeleteModel.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - findRoot, - getFlattenedHierarchy, - fieldReversesReferenceToIndex, - isModel, - isAncestorIndex, - isAncestor, -} from "./hierarchy" -import { $ } from "../common" -import { map, filter, includes } from "lodash/fp" - -export const canDeleteModel = modelNode => { - const flatHierarchy = $(modelNode, [findRoot, getFlattenedHierarchy]) - - const ancestors = $(flatHierarchy, [filter(isAncestor(modelNode))]) - - const belongsToAncestor = i => ancestors.includes(i.parent()) - - const errorsForNode = node => { - const errorsThisNode = $(flatHierarchy, [ - filter( - i => - isAncestorIndex(i) && - belongsToAncestor(i) && - includes(node.nodeId)(i.allowedModelNodeIds) - ), - map( - i => - `index "${i.name}" indexes this model. Please remove the model from the index, or delete the index` - ), - ]) - - for (let child of node.children) { - for (let err of errorsForNode(child)) { - errorsThisNode.push(err) - } - } - - return errorsThisNode - } - - const errors = errorsForNode(modelNode) - - return { errors, canDelete: errors.length === 0 } -} diff --git a/packages/core/src/templateApi/createActions.js b/packages/core/src/templateApi/createActions.js deleted file mode 100644 index 148e6aaa15..0000000000 --- a/packages/core/src/templateApi/createActions.js +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index a232fb68f2..0000000000 --- a/packages/core/src/templateApi/createNodes.js +++ /dev/null @@ -1,242 +0,0 @@ -import { each, find } from "lodash" -import { map, max, constant } from "lodash/fp" -import { - switchCase, - defaultCase, - joinKey, - $, - isNothing, - isSomething, -} from "../common" -import { - isIndex, - isRoot, - isSingleRecord, - isCollectionRecord, - isModel, - 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 => isModel(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 nodeNameMaker = node => () => - isRoot(node) - ? "/" - : joinKey(node.parent().nodeName(), node.name) - -const validate = parent => node => { - if ( - isIndex(node) && - isSomething(parent) && - !isRoot(parent) && - !isModel(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.nodeName = nodeNameMaker(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 (isModel(obj)) { - const defaultIndex = find( - parent.indexes, - i => i.name === `${parent.name}_index` - ) - if (defaultIndex) { - defaultIndex.allowedModelNodeIds.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 _getNewModelTemplate = (parent, name, createDefaultIndex, isSingle) => { - const nodeId = getNodeId(parent) - const node = constructNode(parent, { - name, - type: "record", - fields: [], - children: [], - validationRules: [], - nodeId: nodeId, - indexes: [], - estimatedRecordCount: isModel(parent) ? 500 : 1000000, - collectionName: (nodeId || "").toString(), - isSingle, - }) - - if (createDefaultIndex) { - const defaultIndex = getNewIndexTemplate(parent) - defaultIndex.name = `${name}_index` - defaultIndex.allowedModelNodeIds.push(node.nodeId) - } - - return node -} - -export const getNewModelTemplate = ( - parent, - name = "", - createDefaultIndex = true -) => _getNewModelTemplate(parent, name, createDefaultIndex, false) - -export const getNewSingleRecordTemplate = parent => - _getNewModelTemplate(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: [], - allowedModelNodeIds: [], - 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, - getNewModelTemplate, - getNewIndexTemplate, - createNodeErrors, - constructHierarchy, - getNewAggregateGroupTemplate, - getNewAggregateTemplate, -} diff --git a/packages/core/src/templateApi/deleteAllIndexFilesForNode.js b/packages/core/src/templateApi/deleteAllIndexFilesForNode.js deleted file mode 100644 index 1fae7a4e19..0000000000 --- a/packages/core/src/templateApi/deleteAllIndexFilesForNode.js +++ /dev/null @@ -1,30 +0,0 @@ -import { getAllIdsIterator } from "../indexing/allIds" -import { getRecordInfo } from "../recordApi/recordInfo" -import { isTopLevelIndex, getParentKey, getLastPartInKey } from "./hierarchy" -import { safeKey, joinKey } from "../common" - -export const deleteAllIndexFilesForNode = async (app, indexNode) => { - if (isTopLevelIndex(indexNode)) { - await app.datastore.deleteFolder(indexNode.nodeKey()) - return - } - - const iterate = await getAllIdsIterator(app)(indexNode.parent().nodeKey()) - let iterateResult = await iterate() - while (!iterateResult.done) { - const { result } = iterateResult - for (const id of result.ids) { - const deletingIndexKey = joinKey(result.collectionKey, id, indexNode.name) - await deleteIndexFolder(app, deletingIndexKey) - } - iterateResult = await iterate() - } -} - -const deleteIndexFolder = async (app, indexKey) => { - indexKey = safeKey(indexKey) - const indexName = getLastPartInKey(indexKey) - const parentRecordKey = getParentKey(indexKey) - const recordInfo = getRecordInfo(app.hierarchy, parentRecordKey) - await app.datastore.deleteFolder(joinKey(recordInfo.dir, indexName)) -} diff --git a/packages/core/src/templateApi/deleteAllRecordsForNode.js b/packages/core/src/templateApi/deleteAllRecordsForNode.js deleted file mode 100644 index 3c2f46a7ee..0000000000 --- a/packages/core/src/templateApi/deleteAllRecordsForNode.js +++ /dev/null @@ -1,33 +0,0 @@ -import { getAllIdsIterator } from "../indexing/allIds" -import { getCollectionDir } from "../recordApi/recordInfo" -import { isTopLevelRecord, getCollectionKey } from "./hierarchy" -import { safeKey, joinKey } from "../common" - -export const deleteAllRecordsForNode = async (app, recordNode) => { - if (isTopLevelRecord(recordNode)) { - await deleteRecordCollection(app, recordNode.collectionName) - return - } - - const iterate = await getAllIdsIterator(app)(recordNode.parent().nodeKey()) - let iterateResult = await iterate() - while (!iterateResult.done) { - const { result } = iterateResult - for (const id of result.ids) { - const deletingCollectionKey = joinKey( - result.collectionKey, - id, - recordNode.collectionName - ) - await deleteRecordCollection(app, deletingCollectionKey) - } - iterateResult = await iterate() - } -} - -const deleteRecordCollection = async (app, collectionKey) => { - collectionKey = safeKey(collectionKey) - await app.datastore.deleteFolder( - getCollectionDir(app.hierarchy, collectionKey) - ) -} diff --git a/packages/core/src/templateApi/deleteNodes.js b/packages/core/src/templateApi/deleteNodes.js deleted file mode 100644 index d7e6ad36ae..0000000000 --- a/packages/core/src/templateApi/deleteNodes.js +++ /dev/null @@ -1,9 +0,0 @@ -import { } from "../templateApi/heirarchy" - -export const canDelete = () => { - /* - it must not exist on any index.allowedModelNodeIds - 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/diffHierarchy.js b/packages/core/src/templateApi/diffHierarchy.js deleted file mode 100644 index 074e2e20a7..0000000000 --- a/packages/core/src/templateApi/diffHierarchy.js +++ /dev/null @@ -1,203 +0,0 @@ -import { - getFlattenedHierarchy, - isModel, - isIndex, - isAncestor, -} from "./hierarchy" -import { $, none } from "../common" -import { map, filter, some, find, difference } from "lodash/fp" - -export const HierarchyChangeTypes = { - recordCreated: "Record Created", - recordDeleted: "Record Deleted", - recordRenamed: "Record Renamed", - recordFieldsChanged: "Record Fields Changed", - recordEstimatedRecordTypeChanged: "Record's Estimated Record Count Changed", - indexCreated: "Index Created", - indexDeleted: "Index Deleted", - indexChanged: "Index Changed", -} - -export const diffHierarchy = (oldHierarchy, newHierarchy) => { - const oldHierarchyFlat = getFlattenedHierarchy(oldHierarchy) - const newHierarchyFlat = getFlattenedHierarchy(newHierarchy) - - const createdRecords = findCreatedRecords(oldHierarchyFlat, newHierarchyFlat) - const deletedRecords = findDeletedRecords(oldHierarchyFlat, newHierarchyFlat) - - return [ - ...createdRecords, - ...deletedRecords, - ...findRenamedRecords(oldHierarchyFlat, newHierarchyFlat), - ...findRecordsWithFieldsChanged(oldHierarchyFlat, newHierarchyFlat), - ...findRecordsWithEstimatedRecordTypeChanged( - oldHierarchyFlat, - newHierarchyFlat - ), - ...findCreatedIndexes(oldHierarchyFlat, newHierarchyFlat, createdRecords), - ...findDeletedIndexes(oldHierarchyFlat, newHierarchyFlat, deletedRecords), - ...findUpdatedIndexes(oldHierarchyFlat, newHierarchyFlat), - ] -} - -const changeItem = (type, oldNode, newNode) => ({ - type, - oldNode, - newNode, -}) - -const findCreatedRecords = (oldHierarchyFlat, newHierarchyFlat) => { - const allCreated = $(newHierarchyFlat, [ - filter(isModel), - filter(nodeDoesNotExistIn(oldHierarchyFlat)), - map(n => changeItem(HierarchyChangeTypes.recordCreated, null, n)), - ]) - - return $(allCreated, [ - filter(r => none(r2 => isAncestor(r.newNode)(r2.newNode))(allCreated)), - ]) -} - -const findDeletedRecords = (oldHierarchyFlat, newHierarchyFlat) => { - const allDeleted = $(oldHierarchyFlat, [ - filter(isModel), - filter(nodeDoesNotExistIn(newHierarchyFlat)), - map(n => changeItem(HierarchyChangeTypes.recordDeleted, n, null)), - ]) - - return $(allDeleted, [ - filter(r => none(r2 => isAncestor(r.oldNode)(r2.oldNode))(allDeleted)), - ]) -} - -const findRenamedRecords = (oldHierarchyFlat, newHierarchyFlat) => - $(oldHierarchyFlat, [ - filter(isModel), - filter(nodeExistsIn(newHierarchyFlat)), - filter( - nodeChanged( - newHierarchyFlat, - (_new, old) => _new.collectionKey !== old.collectionKey - ) - ), - map(n => - changeItem( - HierarchyChangeTypes.recordRenamed, - n, - findNodeIn(n, newHierarchyFlat) - ) - ), - ]) - -const findRecordsWithFieldsChanged = (oldHierarchyFlat, newHierarchyFlat) => - $(oldHierarchyFlat, [ - filter(isModel), - filter(nodeExistsIn(newHierarchyFlat)), - filter(hasDifferentFields(newHierarchyFlat)), - map(n => - changeItem( - HierarchyChangeTypes.recordFieldsChanged, - n, - findNodeIn(n, newHierarchyFlat) - ) - ), - ]) - -const findRecordsWithEstimatedRecordTypeChanged = ( - oldHierarchyFlat, - newHierarchyFlat -) => - $(oldHierarchyFlat, [ - filter(isModel), - filter(nodeExistsIn(newHierarchyFlat)), - filter( - nodeChanged( - newHierarchyFlat, - (_new, old) => _new.estimatedRecordCount !== old.estimatedRecordCount - ) - ), - map(n => - changeItem( - HierarchyChangeTypes.recordEstimatedRecordTypeChanged, - n, - findNodeIn(n, newHierarchyFlat) - ) - ), - ]) - -const findCreatedIndexes = ( - oldHierarchyFlat, - newHierarchyFlat, - createdRecords -) => { - const allCreated = $(newHierarchyFlat, [ - filter(isIndex), - filter(nodeDoesNotExistIn(oldHierarchyFlat)), - map(n => changeItem(HierarchyChangeTypes.indexCreated, null, n)), - ]) - - return $(allCreated, [ - filter(r => none(r2 => isAncestor(r.newNode)(r2.newNode))(createdRecords)), - ]) -} - -const findDeletedIndexes = ( - oldHierarchyFlat, - newHierarchyFlat, - deletedRecords -) => { - const allDeleted = $(oldHierarchyFlat, [ - filter(isIndex), - filter(nodeDoesNotExistIn(newHierarchyFlat)), - map(n => changeItem(HierarchyChangeTypes.indexDeleted, n, null)), - ]) - - return $(allDeleted, [ - filter(r => none(r2 => isAncestor(r.oldNode)(r2.oldNode))(deletedRecords)), - ]) -} - -const findUpdatedIndexes = (oldHierarchyFlat, newHierarchyFlat) => - $(oldHierarchyFlat, [ - filter(isIndex), - filter(nodeExistsIn(newHierarchyFlat)), - filter(nodeChanged(newHierarchyFlat, indexHasChanged)), - map(n => - changeItem( - HierarchyChangeTypes.indexChanged, - n, - findNodeIn(n, newHierarchyFlat) - ) - ), - ]) - -const hasDifferentFields = otherFlatHierarchy => record1 => { - const record2 = findNodeIn(record1, otherFlatHierarchy) - - if (record1.fields.length !== record2.fields.length) return true - - for (let f1 of record1.fields) { - if (none(isFieldSame(f1))(record2.fields)) return true - } - - return false -} - -const indexHasChanged = (_new, old) => - _new.map !== old.map || - _new.filter !== old.filter || - _new.getShardName !== old.getShardName || - difference(_new.allowedModelNodeIds)(old.allowedModelNodeIds).length > 0 - -const isFieldSame = f1 => f2 => f1.name === f2.name && f1.type === f2.type - -const nodeDoesNotExistIn = inThis => node => - none(n => n.nodeId === node.nodeId)(inThis) - -const nodeExistsIn = inThis => node => - some(n => n.nodeId === node.nodeId)(inThis) - -const nodeChanged = (inThis, isChanged) => node => - some(n => n.nodeId === node.nodeId && isChanged(n, node))(inThis) - -const findNodeIn = (node, inThis) => find(n => n.nodeId === node.nodeId)(inThis) diff --git a/packages/core/src/templateApi/fields.js b/packages/core/src/templateApi/fields.js deleted file mode 100644 index f83fe63a01..0000000000 --- a/packages/core/src/templateApi/fields.js +++ /dev/null @@ -1,96 +0,0 @@ -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" -import { generate } from "shortid" - -export const fieldErrors = { - AddFieldValidationFailed: "Add field validation: ", -} - -export const allowedTypes = () => keys(all) - -export const getNewField = type => ({ - id: generate(), - 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 deleted file mode 100644 index 88c2eed595..0000000000 --- a/packages/core/src/templateApi/getApplicationDefinition.js +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index cc17666235..0000000000 --- a/packages/core/src/templateApi/getBehaviourSources.js +++ /dev/null @@ -1,3 +0,0 @@ -export const getBehaviourSources = async datastore => { - await datastore.loadFile("/.config/behaviourSources.js") -} diff --git a/packages/core/src/templateApi/getCouchDbMapFunction.js b/packages/core/src/templateApi/getCouchDbMapFunction.js deleted file mode 100644 index 0d7e8a6126..0000000000 --- a/packages/core/src/templateApi/getCouchDbMapFunction.js +++ /dev/null @@ -1,32 +0,0 @@ -import { includes } from "lodash/fp" - -export const getCouchDbView = (hierarchy, indexNode) => { - const filter = codeAsFunction("filter", indexNode.filter) - const map = codeAsFunction("map", indexNode.map) - const allowedIdsFilter - - const includeDocs = !map - - const couchDbMap = `` - -} - -const codeAsFunction = (name, code) => { - if ((code || "").trim().length === 0) return - - let safeCode - - if (includes("return ")(code)) { - safeCode = code - } else { - let trimmed = code.trim() - trimmed = trimmed.endsWith(";") - ? trimmed.substring(0, trimmed.length - 1) - : trimmed - safeCode = `return (${trimmed})` - } - - return `function ${name}() { - ${safeCode} - }` -} \ No newline at end of file diff --git a/packages/core/src/templateApi/hierarchy.js b/packages/core/src/templateApi/hierarchy.js deleted file mode 100644 index 9d7bc3fc6b..0000000000 --- a/packages/core/src/templateApi/hierarchy.js +++ /dev/null @@ -1,290 +0,0 @@ -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 getExactNodeForKey = 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 = getExactNodeForKey(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(getExactNodeForKey(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 => isModel(n) && n.nodeId === getRecordNodeIdFromId(recordId)), - ]) - -export const recordNodeIdIsAllowed = indexNode => nodeId => - indexNode.allowedModelNodeIds.length === 0 || - includes(nodeId)(indexNode.allowedModelNodeIds) - -export const recordNodeIsAllowed = indexNode => recordNode => - recordNodeIdIsAllowed(indexNode)(recordNode.nodeId) - -export const getAllowedRecordNodesForIndex = (appHierarchy, indexNode) => { - const recordNodes = $(appHierarchy, [getFlattenedHierarchy, filter(isModel)]) - - 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 getDependantIndexes = (hierarchy, recordNode) => { - const allIndexes = $(hierarchy, [getFlattenedHierarchy, filter(isIndex)]) - - const allowedAncestors = $(allIndexes, [ - filter(isAncestorIndex), - filter(i => recordNodeIsAllowed(i)(recordNode)), - ]) - - const allowedReference = $(allIndexes, [ - filter(isReferenceIndex), - filter(i => some(fieldReversesReferenceToIndex(i))(recordNode.fields)), - ]) - - return [...allowedAncestors, ...allowedReference] -} - -export const getNodeFromNodeKeyHash = hierarchy => hash => - $(hierarchy, [ - getFlattenedHierarchy, - find(n => getHashCode(n.nodeKey()) === hash), - ]) - -export const isModel = node => isSomething(node) && node.type === "record" -export const isSingleRecord = node => isModel(node) && node.isSingle -export const isCollectionRecord = node => isModel(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 findRoot = node => (isRoot(node) ? node : findRoot(node.parent())) -export const isDecendantOfARecord = hasMatchingAncestor(isModel) -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 isTopLevelRecord = node => isRoot(node.parent()) && isModel(node) -export const isTopLevelIndex = node => isRoot(node.parent()) && isIndex(node) -export const getCollectionKey = recordKey => - $(recordKey, [splitKey, parts => joinKey(parts.slice(0, parts.length - 1))]) -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 const nodeNameFromNodeKey = (hierarchy, nodeKey) => { - const node = getNode(hierarchy, nodeKey) - return node ? node.nodeName() : "" -} - -export default { - getLastPartInKey, - getNodesInPath, - getExactNodeForKey, - hasMatchingAncestor, - getNode, - getNodeByKeyOrNodeKey, - isNode, - getActualKeyOfParent, - getParentKey, - isKeyAncestorOf, - hasNoMatchingAncestors, - findField, - isAncestor, - isDecendant, - getRecordNodeId, - getRecordNodeIdFromId, - getRecordNodeById, - recordNodeIdIsAllowed, - recordNodeIsAllowed, - getAllowedRecordNodesForIndex, - getNodeFromNodeKeyHash, - isModel, - isCollectionRecord, - isIndex, - isaggregateGroup, - isShardedIndex, - isRoot, - isDecendantOfARecord, - isGlobalIndex, - isReferenceIndex, - isAncestorIndex, - fieldReversesReferenceToNode, - fieldReversesReferenceToIndex, - getFlattenedHierarchy, - isTopLevelIndex, - isTopLevelRecord, - nodeNameFromNodeKey, -} diff --git a/packages/core/src/templateApi/index.js b/packages/core/src/templateApi/index.js deleted file mode 100644 index e31109c2e0..0000000000 --- a/packages/core/src/templateApi/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import { - getNewRootLevel, - getNewModelTemplate, - 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" -import { upgradeData } from "./upgradeData" - -const api = app => ({ - getApplicationDefinition: getApplicationDefinition(app.datastore), - saveApplicationHierarchy: saveApplicationHierarchy(app), - saveActionsAndTriggers: saveActionsAndTriggers(app), - getBehaviourSources: () => getBehaviourSources(app.datastore), - getNewRootLevel, - constructNode, - getNewIndexTemplate, - getNewModelTemplate, - getNewField, - validateField, - addField, - fieldErrors, - getNewRecordValidationRule, - commonRecordValidationRules, - addRecordValidationRule, - createAction, - createTrigger, - validateActions, - validateTrigger, - getNewAggregateGroupTemplate, - getNewAggregateTemplate, - constructHierarchy, - getNewSingleRecordTemplate, - allTypes: all, - validateNode, - validateAll, - validateTriggers, - upgradeData: upgradeData(app), -}) - -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 deleted file mode 100644 index a59e638990..0000000000 --- a/packages/core/src/templateApi/indexes.js +++ /dev/null @@ -1,55 +0,0 @@ -import { map, isEmpty, countBy, flatten, includes, join, keys } from "lodash/fp" -import { } from "lodash" -import { applyRuleSet, makerule } from "../common/validationCommon" -import { compileFilter, compileMap } from "../indexing/evaluate" -import { isNonEmptyString, executesWithoutException, $ } from "../common" -import { isModel } 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 => - isModel(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/initialiseNewIndex.js b/packages/core/src/templateApi/initialiseNewIndex.js deleted file mode 100644 index 1299e4cd20..0000000000 --- a/packages/core/src/templateApi/initialiseNewIndex.js +++ /dev/null @@ -1,27 +0,0 @@ -import { getAllIdsIterator } from "../indexing/allIds" -import { getRecordInfo } from "../recordApi/recordInfo" -import { isTopLevelIndex } from "./hierarchy" -import { joinKey } from "../common" -import { initialiseIndex } from "../indexing/initialiseIndex" - -export const initialiseNewIndex = async (app, indexNode) => { - if (isTopLevelIndex(indexNode)) { - await initialiseIndex(app.datastore, "/", indexNode) - return - } - - const iterate = await getAllIdsIterator(app)(indexNode.parent().nodeKey()) - let iterateResult = await iterate() - while (!iterateResult.done) { - const { result } = iterateResult - for (const id of result.ids) { - const recordKey = joinKey(result.collectionKey, id) - await initialiseIndex( - app.datastore, - getRecordInfo(app.hierarchy, recordKey).dir, - indexNode - ) - } - iterateResult = await iterate() - } -} diff --git a/packages/core/src/templateApi/recordValidationRules.js b/packages/core/src/templateApi/recordValidationRules.js deleted file mode 100644 index 90f3d6a5b3..0000000000 --- a/packages/core/src/templateApi/recordValidationRules.js +++ /dev/null @@ -1,46 +0,0 @@ -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 deleted file mode 100644 index 9b130c7960..0000000000 --- a/packages/core/src/templateApi/saveActionsAndTriggers.js +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100644 index a008fd5f5b..0000000000 --- a/packages/core/src/templateApi/saveApplicationHierarchy.js +++ /dev/null @@ -1,45 +0,0 @@ -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 (hierarchy.getFlattenedHierarchy) { - delete hierarchy.getFlattenedHierarchy - } - - 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/upgradeData.js b/packages/core/src/templateApi/upgradeData.js deleted file mode 100644 index ca0c1d4f29..0000000000 --- a/packages/core/src/templateApi/upgradeData.js +++ /dev/null @@ -1,208 +0,0 @@ -import { diffHierarchy, HierarchyChangeTypes } from "./diffHierarchy" -import { $, switchCase } from "../common" -import { - differenceBy, - isEqual, - some, - map, - filter, - uniqBy, - flatten, -} from "lodash/fp" -import { - findRoot, - getDependantIndexes, - isTopLevelRecord, - isAncestorIndex, -} from "./hierarchy" -import { generateSchema } from "../indexing/indexSchemaCreator" -import { _buildIndex } from "../indexApi/buildIndex" -import { constructHierarchy } from "./createNodes" -import { deleteAllRecordsForNode } from "./deleteAllRecordsForNode" -import { deleteAllIndexFilesForNode } from "./deleteAllIndexFilesForNode" -import { cloneApp } from "../appInitialise/cloneApp" -import { initialiseData } from "../appInitialise/initialiseData" -import { initialiseChildrenForNode } from "../recordApi/initialiseChildren" -import { initialiseNewIndex } from "./initialiseNewIndex" -import { _saveApplicationHierarchy } from "../templateApi/saveApplicationHierarchy" -import { getApplicationDefinition } from "../templateApi/getApplicationDefinition" - -export const upgradeData = app => async newHierarchy => { - const currentAppDef = await getApplicationDefinition(app.datastore)() - app.hierarchy = currentAppDef.hierarchy - newHierarchy = constructHierarchy(newHierarchy) - const diff = diffHierarchy(app.hierarchy, newHierarchy) - const changeActions = gatherChangeActions(diff) - - if (changeActions.length === 0) return - - const newApp = - newHierarchy && - cloneApp(app, { - hierarchy: newHierarchy, - }) - await doUpgrade(app, newApp, changeActions) - await _saveApplicationHierarchy(newApp.datastore, newHierarchy) -} - -const gatherChangeActions = diff => - $(diff, [map(actionForChange), flatten, uniqBy(a => a.compareKey)]) - -const doUpgrade = async (oldApp, newApp, changeActions) => { - for (let action of changeActions) { - await action.run(oldApp, newApp, action.diff) - } -} - -const actionForChange = diff => - switchCase( - [isChangeType(HierarchyChangeTypes.recordCreated), recordCreatedAction], - - [isChangeType(HierarchyChangeTypes.recordDeleted), deleteRecordsAction], - - [ - isChangeType(HierarchyChangeTypes.recordFieldsChanged), - rebuildAffectedIndexesAction, - ], - - [isChangeType(HierarchyChangeTypes.recordRenamed), renameRecordAction], - - [ - isChangeType(HierarchyChangeTypes.recordEstimatedRecordTypeChanged), - reshardRecordsAction, - ], - - [isChangeType(HierarchyChangeTypes.indexCreated), newIndexAction], - - [isChangeType(HierarchyChangeTypes.indexDeleted), deleteIndexAction], - - [isChangeType(HierarchyChangeTypes.indexChanged), rebuildIndexAction] - )(diff) - -const isChangeType = changeType => change => change.type === changeType - -const action = (diff, compareKey, run) => ({ - diff, - compareKey, - run, -}) - -const reshardRecordsAction = diff => [ - action(diff, `reshardRecords-${diff.oldNode.nodeKey()}`, runReshardRecords), -] - -const rebuildIndexAction = diff => [ - action(diff, `rebuildIndex-${diff.newNode.nodeKey()}`, runRebuildIndex), -] - -const newIndexAction = diff => { - if (isAncestorIndex(diff.newNode)) { - return [ - action(diff, `rebuildIndex-${diff.newNode.nodeKey()}`, runRebuildIndex), - ] - } else { - return [action(diff, `newIndex-${diff.newNode.nodeKey()}`, runNewIndex)] - } -} - -const deleteIndexAction = diff => [ - action(diff, `deleteIndex-${diff.oldNode.nodeKey()}`, runDeleteIndex), -] - -const deleteRecordsAction = diff => [ - action(diff, `deleteRecords-${diff.oldNode.nodeKey()}`, runDeleteRecords), -] - -const renameRecordAction = diff => [ - action(diff, `renameRecords-${diff.oldNode.nodeKey()}`, runRenameRecord), -] - -const recordCreatedAction = diff => { - if (isTopLevelRecord(diff.newNode)) { - return [action(diff, `initialiseRoot`, runInitialiseRoot)] - } - - return [ - action( - diff, - `initialiseChildRecord-${diff.newNode.nodeKey()}`, - runInitialiseChildRecord - ), - ] -} - -const rebuildAffectedIndexesAction = diff => { - const newHierarchy = findRoot(diff.newNode) - const oldHierarchy = findRoot(diff.oldNode) - const indexes = getDependantIndexes(newHierarchy, diff.newNode) - - const changedFields = (() => { - const addedFields = differenceBy(f => f.name)(diff.oldNode.fields)( - diff.newNode.fields - ) - - const removedFields = differenceBy(f => f.name)(diff.newNode.fields)( - diff.oldNode.fields - ) - - return map(f => f.name)([...addedFields, ...removedFields]) - })() - - const isIndexAffected = i => { - if ( - !isEqual(generateSchema(oldHierarchy, i), generateSchema(newHierarchy, i)) - ) - return true - - if (some(f => indexes.filter.indexOf(`record.${f}`) > -1)(changedFields)) - return true - - if ( - some(f => indexes.getShardName.indexOf(`record.${f}`) > -1)(changedFields) - ) - return true - - return false - } - - return $(indexes, [ - filter(isIndexAffected), - map(i => - action({ newNode: i }, `rebuildIndex-${i.nodeKey()}`, runRebuildIndex) - ), - ]) -} - -const runReshardRecords = async change => { - throw new Error("Resharding of records is not supported yet") -} - -const runRebuildIndex = async (_, newApp, diff) => { - await _buildIndex(newApp, diff.newNode.nodeKey()) -} - -const runDeleteIndex = async (oldApp, _, diff) => { - await deleteAllIndexFilesForNode(oldApp, diff.oldNode) -} - -const runDeleteRecords = async (oldApp, _, diff) => { - await deleteAllRecordsForNode(oldApp, diff.oldNode) -} - -const runNewIndex = async (_, newApp, diff) => { - await initialiseNewIndex(newApp, diff.newNode) -} - -const runRenameRecord = change => { - /* - Going to disllow this in the builder. once a collection key is set... its done - */ -} - -const runInitialiseRoot = async (_, newApp) => { - await initialiseData(newApp.datastore, newApp) -} - -const runInitialiseChildRecord = async (_, newApp, diff) => { - await initialiseChildrenForNode(newApp, diff.newNode) -} diff --git a/packages/core/src/templateApi/validate.js b/packages/core/src/templateApi/validate.js deleted file mode 100644 index 071b42c191..0000000000 --- a/packages/core/src/templateApi/validate.js +++ /dev/null @@ -1,215 +0,0 @@ -import { - filter, - union, - constant, - map, - flatten, - every, - uniqBy, - some, - includes, - isEmpty, - has, -} from "lodash/fp" -import { compileCode } from "../common/compileCode" -import { - $, - isSomething, - switchCase, - anyTrue, - isNonEmptyArray, - executesWithoutException, - isNonEmptyString, - defaultCase, -} from "../common" -import { - isModel, - 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(isModel, 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("messageWhenInvalid")(r))(node.validationRules) - ), - makerule( - "validationRules", - "validation rule is missing a 'expressionWhenValid' member", - node => every(r => has("expressionWhenValid")(r))(node.validationRules) - ), -] - -const aggregateGroupRules = [ - makerule( - "condition", - "condition does not compile", - a => - isEmpty(a.condition) || - executesWithoutException(() => compileCode(a.condition)) - ), -] - -const getRuleSet = node => - switchCase( - [isModel, 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(isModel), - 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 { - compileCode(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 deleted file mode 100644 index e0fc4e9a94..0000000000 --- a/packages/core/src/templateApi/validateAggregate.js +++ /dev/null @@ -1,23 +0,0 @@ -import { flatten, map, isEmpty } from "lodash/fp" -import { compileCode } from "../common/compileCode" -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 deleted file mode 100644 index b1405c196e..0000000000 --- a/packages/core/src/transactions/cleanup.js +++ /dev/null @@ -1,56 +0,0 @@ -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 - - const _cleanupBatch = async () => { - let processed = 0 - const transactions = await retrieve(app) - let i = 1 - 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) - - processed = transactions.length - } - return processed - } - - try { - let count = -1 - while (count !== 0) { - count = await _cleanupBatch() - } - } 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 deleted file mode 100644 index 1d2bbee6e0..0000000000 --- a/packages/core/src/transactions/create.js +++ /dev/null @@ -1,91 +0,0 @@ -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 deleted file mode 100644 index 9b90a66041..0000000000 --- a/packages/core/src/transactions/execute.js +++ /dev/null @@ -1,359 +0,0 @@ -import { - filter, - map, - reduce, - isUndefined, - includes, - flatten, - intersectionBy, - isEqual, - pull, - keys, - differenceBy, - difference, - some, -} 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, - getExactNodeForKey, - getParentKey, -} from "../templateApi/hierarchy" -import { getRecordInfo } from "../recordApi/recordInfo" -import { getIndexDir } from "../indexApi/getIndexDir" -import { _deleteIndex } from "../indexApi/delete" -import { initialiseIndex } from "../indexing/initialiseIndex" - -export const executeTransactions = app => async transactions => { - const recordsByShard = mappedRecordsByIndexShard(app.hierarchy, transactions) - - for (const shard of keys(recordsByShard)) { - if (recordsByShard[shard].isRebuild) { - if (await app.datastore.exists(shard)) - await app.datastore.deleteFile(shard) - await initialiseIndex( - app.datastore, - getParentKey(recordsByShard[shard].indexDir), - recordsByShard[shard].indexNode - ) - } - await applyToShard( - app.hierarchy, - app.datastore, - recordsByShard[shard].indexDir, - 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, - ...indexBuild.toRemove, - ] - - const toWrite = [...created, ...updates.toWrite, ...indexBuild.toWrite] - - const transByShard = {} - - const initialiseShard = t => { - if (isUndefined(transByShard[t.indexShardKey])) { - transByShard[t.indexShardKey] = { - writes: [], - removes: [], - isRebuild: - some(i => i.indexShardKey === t.indexShardKey)(indexBuild.toWrite) || - some(i => i.indexShardKey === t.indexShardKey)(indexBuild.toRemove), - indexDir: t.indexDir, - indexNodeKey: t.indexNode.nodeKey(), - 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, - indexDir: indexNodeAndPath.indexDir, - indexShardKey: getIndexedDataKey( - indexNodeAndPath.indexNode, - indexNodeAndPath.indexDir, - 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 { toWrite: [], toRemove: [] } - const indexNode = transactions.indexNode - - const getIndexDirs = t => { - if (isGlobalIndex(indexNode)) { - return [indexNode.nodeKey()] - } - - if (isReferenceIndex(indexNode)) { - const recordNode = getExactNodeForKey(hierarchy)(t.record.key) - const refFields = $(recordNode.fields, [ - filter(fieldReversesReferenceToIndex(indexNode)), - ]) - const indexDirs = [] - for (const refField of refFields) { - const refValue = t.record[refField.name] - if (isSomething(refValue) && isNonEmptyString(refValue.key)) { - const indexDir = joinKey( - getRecordInfo(hierarchy, refValue.key).dir, - indexNode.name - ) - - if (!includes(indexDir)(indexDirs)) { - indexDirs.push(indexDir) - } - } - } - return indexDirs - } - - const indexKey = joinKey( - getActualKeyOfParent(indexNode.parent().nodeKey(), t.record.key), - indexNode.name - ) - - return [getIndexDir(hierarchy, indexKey)] - } - - return $(buildTransactions, [ - map(t => { - const mappedRecord = evaluate(t.record)(indexNode) - mappedRecord.result = mappedRecord.result || t.record - const indexDirs = getIndexDirs(t) - return $(indexDirs, [ - map(indexDir => ({ - mappedRecord, - indexNode, - indexDir, - indexShardKey: getIndexedDataKey( - indexNode, - indexDir, - mappedRecord.result - ), - })), - ]) - }), - flatten, - reduce( - (obj, res) => { - if (res.mappedRecord.passedFilter) obj.toWrite.push(res) - else obj.toRemove.push(res) - return obj - }, - { toWrite: [], toRemove: [] } - ), - ]) -} - -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, - indexDir: n.indexDir, - indexShardKey: getIndexedDataKey( - n.indexNode, - n.indexDir, - 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.indexDir, oldIndexes, newIndexes) - - const newlyReferenced = differenceBy(i => i.indexDir, newIndexes, oldIndexes) - - const notChanged = intersectionBy(i => i.indexDir, newIndexes, oldIndexes) - - return { - unReferenced, - newlyReferenced, - notChanged, - } -} diff --git a/packages/core/src/transactions/retrieve.js b/packages/core/src/transactions/retrieve.js deleted file mode 100644 index 809f69acd2..0000000000 --- a/packages/core/src/transactions/retrieve.js +++ /dev/null @@ -1,215 +0,0 @@ -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 - ) - - if (some(isBuildIndexFolder)(transactionFiles)) { - const buildIndexFolders = filter(isBuildIndexFolder)(transactionFiles) - let currentFolderIndex = 0 - while (currentFolderIndex < buildIndexFolders.length) { - const buildIndexFolder = buildIndexFolders[currentFolderIndex] - const transactions = await retrieveBuildIndexTransactions( - app, - joinKey(TRANSACTIONS_FOLDER, buildIndexFolder) - ) - if (transactions.length === 0) { - await app.datastore.deleteFolder( - joinKey(TRANSACTIONS_FOLDER, buildIndexFolder) - ) - } else { - return transactions - } - currentFolderIndex += 1 - } - - return [] - } - - return await retrieveStandardTransactions(app, transactionFiles) -} - -const retrieveBuildIndexTransactions = async (app, buildIndexFolder) => { - const childFolders = await app.datastore.getFolderContents(buildIndexFolder) - const childFolderCount = childFolders.length - if (childFolderCount === 0) { - return [] - } - - const getTransactionFiles = async (childFolderIndex = 0) => { - if (childFolderIndex >= childFolders.length) { - return { childFolderKey: "", files: [] } - } - - const childFolderKey = joinKey( - buildIndexFolder, - childFolders[childFolderIndex] - ) - const files = await app.datastore.getFolderContents(childFolderKey) - - if (files.length > 0) { - return { childFolderKey, files } - } - - await app.datastore.deleteFolder(childFolderKey) - return await getTransactionFiles(childFolderIndex + 1) - } - - 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/setCleanupFunc.js b/packages/core/src/transactions/setCleanupFunc.js deleted file mode 100644 index 1dc9b6a5b6..0000000000 --- a/packages/core/src/transactions/setCleanupFunc.js +++ /dev/null @@ -1,14 +0,0 @@ -import { cleanup } from "./cleanup" - -export const setCleanupFunc = (app, cleanupTransactions) => { - if (cleanupTransactions) { - app.cleanupTransactions = cleanupTransactions - return - } - - if (!app.cleanupTransactions || app.cleanupTransactions.isDefault) { - const newCleanup = async () => cleanup(app) - newCleanup.isDefault = true - app.cleanupTransactions = newCleanup - } -} diff --git a/packages/core/src/transactions/transactionsCommon.js b/packages/core/src/transactions/transactionsCommon.js deleted file mode 100644 index 134ea8c044..0000000000 --- a/packages/core/src/transactions/transactionsCommon.js +++ /dev/null @@ -1,52 +0,0 @@ -import { joinKey, keySep, getHashCode } from "../common" -import { getLastPartInKey } from "../templateApi/hierarchy" -import { includes } from "lodash/fp" - -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 => includes(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_Or_Rebuild = isOfType( - UPDATE_RECORD_TRANSACTION, - BUILD_INDEX_TRANSACTION -) -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 deleted file mode 100644 index f89887c572..0000000000 --- a/packages/core/src/types/array.js +++ /dev/null @@ -1,68 +0,0 @@ -import { map, constant, isArray } 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 deleted file mode 100644 index ff9c7c919d..0000000000 --- a/packages/core/src/types/bool.js +++ /dev/null @@ -1,47 +0,0 @@ -import { constant, isBoolean, isNull } from "lodash/fp" -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 deleted file mode 100644 index 6f6a6b7958..0000000000 --- a/packages/core/src/types/datetime.js +++ /dev/null @@ -1,82 +0,0 @@ -import { constant, isDate, isString, isNull } from "lodash/fp" -import { - makerule, - typeFunctions, - parsedFailed, - parsedSuccess, - getDefaultExport, -} from "./typeHelpers" -import { switchCase, defaultCase, toDateOrNull, isNonEmptyArray } 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 isNullOrEmpty = d => - isNull(d) - || (d || "").toString() === "" - -const isDateOrEmpty = d => - isDate(d) - || isNullOrEmpty(d) - -const dateTryParse = switchCase( - [isDateOrEmpty, parsedSuccess], - [isString, parseStringToDate], - [defaultCase, parsedFailed] -) - -const options = { - maxValue: { - defaultValue: null, - //defaultValue: new Date(32503680000000), - isValid: isDateOrEmpty, - requirementDescription: "must be a valid date", - parse: toDateOrNull, - }, - minValue: { - defaultValue: null, - //defaultValue: new Date(-8520336000000), - isValid: isDateOrEmpty, - requirementDescription: "must be a valid date", - parse: toDateOrNull, - }, -} - -const typeConstraints = [ - makerule( - async (val, opts) => - val === null || isNullOrEmpty(opts.minValue) || val >= opts.minValue, - (val, opts) => - `value (${val.toString()}) must be greater than or equal to ${ - opts.minValue - }` - ), - makerule( - async (val, opts) => - val === null || isNullOrEmpty(opts.maxValue) || 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 deleted file mode 100644 index 656d74a412..0000000000 --- a/packages/core/src/types/file.js +++ /dev/null @@ -1,56 +0,0 @@ -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 deleted file mode 100644 index b8ac8c46c4..0000000000 --- a/packages/core/src/types/index.js +++ /dev/null @@ -1,85 +0,0 @@ -import { assign, merge } from "lodash" -import { - map, - isString, - isNumber, - isBoolean, - isDate, - keys, - isObject, - isArray, - has, -} 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(typeName)(all)) - 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(field.name)(record) - ? 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("key")(value) && has("value")(value)) - return reference - if (isObject(value) && has("relativePath")(value) && has("size")(value)) - 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 deleted file mode 100644 index 545c02a540..0000000000 --- a/packages/core/src/types/number.js +++ /dev/null @@ -1,94 +0,0 @@ -import { constant, isNumber, isString, isNull } from "lodash/fp" -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 deleted file mode 100644 index b2dcc7f1c4..0000000000 --- a/packages/core/src/types/object.js +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index ba7b9b588c..0000000000 --- a/packages/core/src/types/reference.js +++ /dev/null @@ -1,91 +0,0 @@ -import { isString, isObjectLike, isNull, has, isEmpty } from "lodash/fp" -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(path)(ob) && isString(ob[path]) - -const isObjectWithKey = v => isObjectLike(v) && hasStringValue(v, "key") - -const tryParseFromString = s => { - try { - const asObj = JSON.parse(s) - if (isObjectWithKey) { - return parsedSuccess(asObj) - } - } catch (_) { - // EMPTY - } - - return parsedFailed(s) -} - -const referenceTryParse = v => - switchCase( - [isObjectWithKey, parsedSuccess], - [isString, tryParseFromString], - [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 deleted file mode 100644 index c61103273d..0000000000 --- a/packages/core/src/types/string.js +++ /dev/null @@ -1,74 +0,0 @@ -import { constant, isString, isNull, includes, isBoolean } from "lodash/fp" -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 array 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(val)(opts.values), - 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 deleted file mode 100644 index e07552a62a..0000000000 --- a/packages/core/src/types/typeHelpers.js +++ /dev/null @@ -1,94 +0,0 @@ -import { merge } from "lodash" -import { constant, isUndefined, has, mapValues, cloneDeep } from "lodash/fp" -import { isNotEmpty } from "../common" - -export const getSafeFieldParser = (tryParse, defaultValueFunctions) => ( - field, - record -) => { - if (has(field.name)(record)) { - 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(getInitialValue)(defaultValueFunctions) - ? 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 deleted file mode 100644 index fe6328570b..0000000000 --- a/packages/core/test/actionsApi.execute.spec.js +++ /dev/null @@ -1,112 +0,0 @@ -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([]) - }) -}) diff --git a/packages/core/test/apiWrapper.spec.js b/packages/core/test/apiWrapper.spec.js deleted file mode 100644 index 9b0a575871..0000000000 --- a/packages/core/test/apiWrapper.spec.js +++ /dev/null @@ -1,169 +0,0 @@ -import { apiWrapper, apiWrapperSync } from "../src/common/apiWrapper" -import { filter } from "lodash/fp" -import { 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 = () => { - 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 () => { - 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" - ) - }) -}) diff --git a/packages/core/test/authApi.authenticate.spec.js b/packages/core/test/authApi.authenticate.spec.js deleted file mode 100644 index 363c6d0e77..0000000000 --- a/packages/core/test/authApi.authenticate.spec.js +++ /dev/null @@ -1,144 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, -} from "./specHelpers" -import { permissionTypes, userAuthFile } from "../src/authApi/authCommon" -import { permission } from "../src/authApi/permissions" - -describe("authApi > authenticate", () => { - fit("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 } = 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 -} diff --git a/packages/core/test/authApi.changePassword.spec.js b/packages/core/test/authApi.changePassword.spec.js deleted file mode 100644 index 11b5926570..0000000000 --- a/packages/core/test/authApi.changePassword.spec.js +++ /dev/null @@ -1,160 +0,0 @@ -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 deleted file mode 100644 index f9cd2681c0..0000000000 --- a/packages/core/test/authApi.createAccessLevels.spec.js +++ /dev/null @@ -1,214 +0,0 @@ -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 -} diff --git a/packages/core/test/authApi.createUser.spec.js b/packages/core/test/authApi.createUser.spec.js deleted file mode 100644 index f5d04abeb8..0000000000 --- a/packages/core/test/authApi.createUser.spec.js +++ /dev/null @@ -1,215 +0,0 @@ -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 -} diff --git a/packages/core/test/authApi.disableUser.spec.js b/packages/core/test/authApi.disableUser.spec.js deleted file mode 100644 index 2b4f9c40b7..0000000000 --- a/packages/core/test/authApi.disableUser.spec.js +++ /dev/null @@ -1,157 +0,0 @@ -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), - ]) diff --git a/packages/core/test/authApi.setUserAccesslLevels.spec.js b/packages/core/test/authApi.setUserAccesslLevels.spec.js deleted file mode 100644 index 73bc3c7d7d..0000000000 --- a/packages/core/test/authApi.setUserAccesslLevels.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -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), - ]) diff --git a/packages/core/test/collectionApi.delete.spec.js b/packages/core/test/collectionApi.delete.spec.js deleted file mode 100644 index 8841cbc051..0000000000 --- a/packages/core/test/collectionApi.delete.spec.js +++ /dev/null @@ -1,81 +0,0 @@ -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, appHierarchy } = 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([ - "/customers", - `/customers/${appHierarchy.customerRecord.nodeId}`, - ]) - }) - - 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 + 2 - // +2 because is should keep the collection folders: /customers & /customers/1 - - 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") - }) -}) diff --git a/packages/core/test/collectionApi.spec.js b/packages/core/test/collectionApi.spec.js deleted file mode 100644 index de47e0f4d1..0000000000 --- a/packages/core/test/collectionApi.spec.js +++ /dev/null @@ -1,200 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, -} 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() - }) -}) diff --git a/packages/core/test/common.spec.js b/packages/core/test/common.spec.js deleted file mode 100644 index cd850dcb1f..0000000000 --- a/packages/core/test/common.spec.js +++ /dev/null @@ -1,216 +0,0 @@ -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) - }) -}) diff --git a/packages/core/test/couchDb.js b/packages/core/test/couchDb.js deleted file mode 100644 index 5b507293fe..0000000000 --- a/packages/core/test/couchDb.js +++ /dev/null @@ -1,107 +0,0 @@ -import { isUndefined, isString } from "lodash" -import initialiseNano from "nano" - -export const getTestDb = async () => { - const nano = initialiseNano("http://admin:password@127.0.0.1:5984") - try { - await nano.db.destroy("unit_tests") - } catch (_) { - // do nothing - } - await nano.db.create("unit_tests") - const db = nano.use("unit_tests") - await db.insert({ _id: "/", folderMarker, items: [] }) - return db -} - -const folderMarker = "OH-YES-ITSA-FOLDER-" -const isFolder = val => { - if (isUndefined(val)) { - throw new Error("Passed undefined value for folder") - } - return val.folderMarker === folderMarker -} - -export const createFile = db => async (key, content) => { - return await db.insert({ _id: key, ...content }) -} - -export const updateFile = db => async (key, content) => { - if (!content._rev) { - throw new Error("not an update: no _rev supplied") - } - return await db.insert({ _id: key, ...content }) -} - -export const writableFileStream = db => async key => { - throw new Error("WRITABLE STREAM: souldn't need this") -} - -export const readableFileStream = db => async key => { - throw new Error("READABLE STREAM: souldn't need this") -} - -export const getFileSize = data => async path => { - throw new Error("GET FILE SIZE: should'nt need this") -} - -export const renameFile = db => async (oldKey, newKey) => { - // used by indexing and Files - wont be needed - throw new Error( - "RENAME FILE: not clear how to do this in CouchDB - we probably dont need it" - ) -} - -export const loadFile = db => async key => { - return await db.get(key) -} - -export const exists = db => async key => { - try { - await db.head(key) - return true - } catch (_) { - return false - } -} - -export const deleteFile = db => async keyOrDoc => { - const doc = isString(keyOrDoc) ? await db.get(keyOrDoc) : keyOrDoc - const key = isString(keyOrDoc) ? keyOrDoc : doc._id - if (isFolder(doc)) - throw new Error("DeleteFile: Path " + key + " is a folder, not a file") - await db.destroy(key) -} -export const createFolder = db => async key => { - await db.insert({ _id: key, folderMarker, items: [] }) -} - -export const deleteFolder = db => async keyOrDoc => { - throw new Error("DELETE FOLDER: should not be needed") -} - -export const getFolderContents = db => async key => { - const doc = await db.get(key) - if (!isFolder(doc)) throw new Error("Not a folder: " + key) - return doc.items -} - -export default db => { - return { - createFile: createFile(db), - updateFile: updateFile(db), - loadFile: loadFile(db), - exists: exists(db), - deleteFile: deleteFile(db), - createFolder: createFolder(db), - deleteFolder: deleteFolder(db), - readableFileStream: readableFileStream(db), - writableFileStream: writableFileStream(db), - renameFile: renameFile(db), - getFolderContents: getFolderContents(db), - getFileSize: getFileSize(db), - datastoreType: "couchdb", - datastoreDescription: "", - data: db, - } -} diff --git a/packages/core/test/getApis.spec.js b/packages/core/test/getApis.spec.js deleted file mode 100644 index 32ed07da11..0000000000 --- a/packages/core/test/getApis.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -import { getAppApis } from "../src" -import { - getMemoryTemplateApi, - createAppDefinitionWithActionsAndTriggers, -} from "./specHelpers" -import { isFunction } from "lodash" - -describe("getAppApis", () => { - const getMemoryAppApis = async () => { - const { templateApi } = await 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() - }) -}) diff --git a/packages/core/test/indexApi.aggregates.spec.js b/packages/core/test/indexApi.aggregates.spec.js deleted file mode 100644 index 65f45c0d45..0000000000 --- a/packages/core/test/indexApi.aggregates.spec.js +++ /dev/null @@ -1,159 +0,0 @@ -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 } -} diff --git a/packages/core/test/indexApi.buildIndex.spec.js b/packages/core/test/indexApi.buildIndex.spec.js deleted file mode 100644 index e6f4284d98..0000000000 --- a/packages/core/test/indexApi.buildIndex.spec.js +++ /dev/null @@ -1,610 +0,0 @@ -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 { getExactNodeForKey } 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 = getExactNodeForKey(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 = getExactNodeForKey(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() - ) - }) -}) diff --git a/packages/core/test/indexApi.list.spec.js b/packages/core/test/indexApi.list.spec.js deleted file mode 100644 index 0c65c03b0f..0000000000 --- a/packages/core/test/indexApi.list.spec.js +++ /dev/null @@ -1,161 +0,0 @@ -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") - }) -}) diff --git a/packages/core/test/indexing.concurrency.spec.js b/packages/core/test/indexing.concurrency.spec.js deleted file mode 100644 index a4a5261970..0000000000 --- a/packages/core/test/indexing.concurrency.spec.js +++ /dev/null @@ -1,288 +0,0 @@ -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" -import { getRecordInfo } from "../src/recordApi/recordInfo" - -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" - - const recordInfo = getRecordInfo(app.hierarchy, savedRecord.key) - await recordApi._storeHandle.updateJson( - recordInfo.child("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" - - const recordInfo = getRecordInfo(app.hierarchy, savedRecord.key) - await recordApi._storeHandle.updateJson( - recordInfo.child("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" - 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" - 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) - }) -}) diff --git a/packages/core/test/indexing.createIndexFile.spec.js b/packages/core/test/indexing.createIndexFile.spec.js deleted file mode 100644 index bb940dc830..0000000000 --- a/packages/core/test/indexing.createIndexFile.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -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("") - }) -}) diff --git a/packages/core/test/indexing.evaluate.spec.js b/packages/core/test/indexing.evaluate.spec.js deleted file mode 100644 index ba9740204e..0000000000 --- a/packages/core/test/indexing.evaluate.spec.js +++ /dev/null @@ -1,131 +0,0 @@ -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() - }) -}) diff --git a/packages/core/test/indexing.getRelevantIndexes.spec.js b/packages/core/test/indexing.getRelevantIndexes.spec.js deleted file mode 100644 index e53eef0b5f..0000000000 --- a/packages/core/test/indexing.getRelevantIndexes.spec.js +++ /dev/null @@ -1,170 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields_AndIndexes, -} from "./specHelpers" -import { - getRelevantReverseReferenceIndexes, - getRelevantAncestorIndexes, -} from "../src/indexing/relevant" -import { some } from "lodash" -import { joinKey } from "../src/common" -import { getRecordInfo } from "../src/recordApi/recordInfo" - -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.indexDir === key) - - expect(indexExists("/customer_index")).toBeTruthy() - expect(indexExists("/deceased")).toBeTruthy() - expect(indexExists("/customersBySurname")).toBeTruthy() - }) - - it("should ignore index when allowedModelNodeIds 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.indexDir === key) - - expect(indexExists("/customersBySurname")).toBeFalsy() - }) - - it("should include index when allowedModelNodeIds 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.indexDir === 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) - const { dir } = getRecordInfo( - appHierarchy.root, - `/customers/${nodeid}-1234` - ) - expect(indexes.length).toBe(4) - expect(some(indexes, i => i.indexDir === `/customer_invoices`)).toBeTruthy() - expect( - some(indexes, i => i.indexDir === `${dir}/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) - const partnerdir = getRecordInfo(appHierarchy.root, partner.key).dir - expect(indexes[0].indexDir).toBe( - joinKey(partnerdir, 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 - ) - - const referredByCustomerDir = getRecordInfo( - appHierarchy.root, - referredByCustomer.key - ).dir - - expect(indexes.length).toBe(1) - expect(indexes[0].indexDir).toBe( - joinKey( - referredByCustomerDir, - appHierarchy.referredToCustomersReverseIndex.name - ) - ) - }) -}) diff --git a/packages/core/test/indexing.schema.spec.js b/packages/core/test/indexing.schema.spec.js deleted file mode 100644 index 7fa612220f..0000000000 --- a/packages/core/test/indexing.schema.spec.js +++ /dev/null @@ -1,134 +0,0 @@ -import { generateSchema } from "../src/indexing/indexSchemaCreator" -import { setupApphierarchy } 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.getNewModelTemplate(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.allowedModelNodeIds = [dogRecord.nodeId] - - const addDogField = addField(dogRecord) - addDogField("name", "string") - addDogField("dob", "datetime") - addDogField("isAlive", "bool") - - let fishStuff = {} - if (includeFish) { - const fishRecord = templateApi.getNewModelTemplate(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.allowedModelNodeIds = [fishRecord.nodeId] - fishStuff.fishOnlyIndex = fishOnlyIndex - - const dogFriends = templateApi.getNewIndexTemplate( - dogRecord, - indexTypes.reference - ) - dogFriends.name = "dogFriends" - fishStuff.dogFriends = dogFriends - - petsIndex.allowedModelNodeIds.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 deleted file mode 100644 index 6319e83582..0000000000 --- a/packages/core/test/initialiseData.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -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 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") - }) - - it("should initialise 'single record' type nodes", async () => { - const { appDef, datastore } = getApplicationDefinition() - await initialiseData(datastore, appDef) - expect(await datastore.exists(`/settings`)).toBeTruthy() - const settings = await datastore.loadJson("/settings/record.json") - expect(settings.type).toBe("settings") - }) - - const getApplicationDefinition = () => { - const { templateApi, app } = await getMemoryTemplateApi() - const h = basicAppHierarchyCreator_WithFields_AndIndexes(templateApi) - return { - appDef: { hierarchy: h.root, actions: [], triggers: [] }, - datastore: app.datastore, - h, - } - } -}) diff --git a/packages/core/test/memory.js b/packages/core/test/memory.js deleted file mode 100644 index 119413e12d..0000000000 --- a/packages/core/test/memory.js +++ /dev/null @@ -1,178 +0,0 @@ -import { isUndefined, has } from "lodash" -import { take } from "lodash/fp" -import { Readable, Writable } from "readable-stream" -import { Buffer } from "safe-buffer" -import { splitKey, joinKey, $, keySep, getFileFromKey } from "../src/common" -import { getLastPartInKey } from "../src/templateApi/hierarchy" - -const folderMarker = "OH-YES-ITSA-FOLDER-" -const isFolder = val => { - if (isUndefined(val)) { - throw new Error("Passed undefined value for folder") - } - return val.includes(folderMarker) -} - -const getParentFolderKey = key => - $(key, [splitKey, take(splitKey(key).length - 1), joinKey]) - -const getParentFolder = (data, key) => { - if (key === keySep) return null - const parentKey = getParentFolderKey(key) - if (parentKey === keySep) return null - 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"); - if (!getParentFolder(data, path)) { - throw new Error("Parent folder for " + 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() - } - - addItemToParentFolder(data, path) - - 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] - - const parent = getParentFolder(data, newKey) - const oldFileName = getFileFromKey(oldKey) - const newFileName = getFileFromKey(newKey) - parent.items = [...parent.items.filter(i => i !== oldFileName), newFileName] - data[getParentFolderKey(newKey)] = JSON.stringify(parent) -} - -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") - - for (let item of JSON.parse(data[path]).items) { - const fullItemPath = `${path}/${item}` - if (isFolder(data[fullItemPath])) { - await deleteFolder(data)(fullItemPath) - } else { - await deleteFile(data)(fullItemPath) - } - } - - const parent = getParentFolder(data, path) - if (parent) { - parent.items = parent.items.filter(f => f !== getLastPartInKey(path)) - data[getParentFolderKey(path)] = JSON.stringify(parent) - } - - delete data[path] -} - -export const getFolderContents = data => async folderPath => { - if (!(await exists(data)(folderPath))) - throw new Error("Folder does not exist: " + folderPath) - if (!isFolder(data[folderPath])) - throw new Error("Not a folder: " + 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, - } -} diff --git a/packages/core/test/recordApi.customId.spec.js b/packages/core/test/recordApi.customId.spec.js deleted file mode 100644 index 42fa9267f7..0000000000 --- a/packages/core/test/recordApi.customId.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, -} from "./specHelpers" - -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 deleted file mode 100644 index c16d0789c6..0000000000 --- a/packages/core/test/recordApi.delete.spec.js +++ /dev/null @@ -1,73 +0,0 @@ -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 deleted file mode 100644 index 184ddc1baf..0000000000 --- a/packages/core/test/recordApi.files.spec.js +++ /dev/null @@ -1,110 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, -} from "./specHelpers" -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 } = 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 } = 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 < downloadedBytes.length; i++) { - expect(downloadedBytes[i]).toEqual(content[i]) - } - }) - - it("upload should fail when filename contains invalid characters", async () => { - const { recordApi } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - const { file, stream } = 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 } = 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() - }) -}) diff --git a/packages/core/test/recordApi.getContext.spec.js b/packages/core/test/recordApi.getContext.spec.js deleted file mode 100644 index 085c738440..0000000000 --- a/packages/core/test/recordApi.getContext.spec.js +++ /dev/null @@ -1,101 +0,0 @@ -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.getNew.spec.js b/packages/core/test/recordApi.getNew.spec.js deleted file mode 100644 index 186945c9cd..0000000000 --- a/packages/core/test/recordApi.getNew.spec.js +++ /dev/null @@ -1,113 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, - getNewFieldAndAdd, -} from "./specHelpers" -import { isNonEmptyString } from "../src/common" -import { isBoolean } from "util" -import { permission } from "../src/authApi/permissions" -import { _getNew } from "../src/recordApi/getNew" - -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 { 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") - }) - - it("for 'single record' type, should create with key ending in node name", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - const { settingsRecord } = appHierarchy - const result = _getNew(settingsRecord, "") - expect(result.key).toBe("/settings") - }) -}) diff --git a/packages/core/test/recordApi.getRecordInfo.spec.js b/packages/core/test/recordApi.getRecordInfo.spec.js deleted file mode 100644 index afccbbf0a4..0000000000 --- a/packages/core/test/recordApi.getRecordInfo.spec.js +++ /dev/null @@ -1,155 +0,0 @@ -import { folderStructureArray } from "../src/indexing/allIds" -import { getRecordInfo } from "../src/recordApi/recordInfo" -import { setupApphierarchy } from "./specHelpers" - -describe("getRecordInfo", () => { - it("dir should not be sharded when record count = 1000", async () => { - const { root } = (await setup({ parentCount: 1000 })).appHierarchy - const { dir } = getRecordInfo(root, "/parents/1-abcd") - expect(dir).toBe("/parents/1/1-abcd") - }) - - it("dir should be sharded when record count = 1001", async () => { - const { root } = (await setup({ parentCount: 1001 })).appHierarchy - const { dir } = getRecordInfo(root, "/parents/1-abcd") - expect(dir).toBe("/parents/1/0123456789abcdefghijklmnopqrstuv/1-abcd") - }) - - it("dir should be sharded to one char per folder when record count = 63,000 (64*1000)", async () => { - const { root } = (await setup({ parentCount: 64000 })).appHierarchy - const { dir } = getRecordInfo(root, "/parents/1-abcd") - expect(dir).toBe("/parents/1/a/1-abcd") - }) - - it("dir should be sharded to one char per folder, on 2 levels when record count = 4096000 (64*64*1000)", async () => { - const { root } = (await setup({ parentCount: 4096000 })).appHierarchy - const { dir } = getRecordInfo(root, "/parents/1-abcd") - expect(dir).toBe("/parents/1/a/b/1-abcd") - }) - - it("child dir should not be sharded when record count = 1000", async () => { - const { root, child } = ( - await setup({ parentCount: 4096000, childCount: 1000 }) - ).appHierarchy - const { dir } = getRecordInfo( - root, - `/parents/1-abcd/children/${child.nodeId}-defg` - ) - expect(dir).toBe( - `/parents/1/a/b/1-abcd/children/${child.nodeId}/${child.nodeId}-defg` - ) - }) - - it("grandchild dir should not be sharded when record count = 1000", async () => { - const { root, child, grandchild } = ( - await setup({ parentCount: 4096000, childCount: 4096000 }) - ).appHierarchy - const { dir } = getRecordInfo( - root, - `/parents/1-abcd/children/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}-hijk` - ) - expect(dir).toBe( - `/parents/1/a/b/1-abcd/children/${child.nodeId}/d/e/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}/${grandchild.nodeId}-hijk` - ) - }) - - it("grandchild dir should be sharded when record count = 4096000", async () => { - const { root, child, grandchild } = ( - await setup({ - parentCount: 4096000, - childCount: 4096000, - grandChildCount: 4096000, - }) - ).appHierarchy - const { dir } = getRecordInfo( - root, - `/parents/1-abcd/children/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}-hijk` - ) - expect(dir).toBe( - `/parents/1/a/b/1-abcd/children/${child.nodeId}/d/e/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}/h/i/${grandchild.nodeId}-hijk` - ) - }) - - it("child levels can be sharded, with parent not", async () => { - const { root, child, grandchild } = ( - await setup({ - parentCount: 1000, - childCount: 4096000, - grandChildCount: 4096000, - }) - ).appHierarchy - const { dir } = getRecordInfo( - root, - `/parents/1-abcd/children/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}-hijk` - ) - expect(dir).toBe( - `/parents/1/1-abcd/children/${child.nodeId}/d/e/${child.nodeId}-defg/grandchildren/${grandchild.nodeId}/h/i/${grandchild.nodeId}-hijk` - ) - }) -}) - -describe("folderStructureArray", () => { - const recordNode = count => ({ estimatedRecordCount: count }) - - it("should return [] when folder count < 1000", () => { - const result = folderStructureArray(recordNode(999)) - expect(result).toEqual([]) - }) - - it("should return [4] when folder count between 3000 - 4000", () => { - const result = folderStructureArray(recordNode(3456)) - expect(result).toEqual([4]) - }) - - it("should return [64, 2] when folder count between 64000 - 65000", () => { - const result = folderStructureArray(recordNode(64001)) - expect(result).toEqual([64, 2]) - }) - - it("should return [64, 64] when folder = 4095999", () => { - const result = folderStructureArray(recordNode(4095999)) - expect(result).toEqual([64, 64]) - }) - - it("should return [64, 64] when folder = 4096000", () => { - const result = folderStructureArray(recordNode(4096000)) - expect(result).toEqual([64, 64]) - }) - - it("should return [64, 64, 2] when folder = 4096001", () => { - const result = folderStructureArray(recordNode(4096001)) - expect(result).toEqual([64, 64, 2]) - }) -}) - -const setup = ({ parentCount, childCount, grandChildCount }) => - setupApphierarchy(templateApi => { - const root = templateApi.getNewRootLevel() - - const addField = recordNode => { - const field = templateApi.getNewField("string") - field.name = "test" - templateApi.addField(recordNode, field) - return field - } - - const parent = templateApi.getNewModelTemplate(root, "parent") - parent.estimatedRecordCount = parentCount || 1000 - parent.collectionName = "parents" - addField(parent) - const child = templateApi.getNewModelTemplate(parent, "child") - child.estimatedRecordCount = childCount || 1000 - child.collectionName = "children" - addField(child) - const grandchild = templateApi.getNewModelTemplate(child, "grandchild") - grandchild.estimatedRecordCount = grandChildCount || 1000 - grandchild.collectionName = "grandchildren" - addField(grandchild) - - return { - parent, - child, - grandchild, - root, - } - }) diff --git a/packages/core/test/recordApi.reindex.spec.js b/packages/core/test/recordApi.reindex.spec.js deleted file mode 100644 index 24280f2e23..0000000000 --- a/packages/core/test/recordApi.reindex.spec.js +++ /dev/null @@ -1,646 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, - basicAppHierarchyCreator_WithFields_AndIndexes, -} from "./specHelpers" -import { joinKey } from "../src/common" -import { some, isArray, isObjectLike } 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 reference field to index and reparse", async () => { - const { recordApi, indexApi } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - const partner = recordApi.getNew("/partners", "partner") - partner.businessName = "ACME" - partner.phone = "098766e6" - await recordApi.save(partner) - - const customer = recordApi.getNew("/customers", "customer") - customer.surname = "Ledog" - customer.age = 9 - ;(customer.isalive = true), (customer.createdDate = new Date()) - customer.partner = partner - await recordApi.save(customer) - - const customers = await indexApi.listItems("/customer_index") - - expect(customers.length).toBe(1) - expect(isObjectLike(customer.partner)).toBeTruthy() - expect(customers[0].partner.key).toBe(partner.key) - expect(customers[0].partner.name).toBe(partner.businessName) - expect(customers[0].partner.phone).toBe(partner.phone) - }) - - 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 } = 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) - }) -}) diff --git a/packages/core/test/recordApi.save.spec.js b/packages/core/test/recordApi.save.spec.js deleted file mode 100644 index 6912367fbe..0000000000 --- a/packages/core/test/recordApi.save.spec.js +++ /dev/null @@ -1,267 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, - stubEventHandler, -} from "./specHelpers" -import { events, isNonEmptyString } from "../src/common" -import { permission } from "../src/authApi/permissions" -import { getRecordInfo } from "../src/recordApi/recordInfo" - -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 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) - }) - - it("should maintain referential integrity", async () => { - const { recordApi } = 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 = referredByCustomer - await recordApi.save(referredCustomer) - - const savedReferredBy = recordApi.load(referredByCustomer.key) - savedReferredBy.surname = "Zeedog" - await recordApi.save(savedReferredBy) - - const loadedReferredTo = await recordApi.load(referredCustomer.key) - expect(loadedReferredTo.referredBy.surname).toBe("Zeedog") - }) -}) - -describe("save", () => { - - 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" - - 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) - - const savedRecord = await recordApi.load(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("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 deleted file mode 100644 index 93476ed2b7..0000000000 --- a/packages/core/test/recordApi.validate.spec.js +++ /dev/null @@ -1,299 +0,0 @@ -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 => { - const surname = find( - hierarchy.customerRecord.fields, - f => f.name === "surname" - ) - surname.typeOptions.maxLength = 5 - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = 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 => { - 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 } = 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 => { - const age = find(hierarchy.customerRecord.fields, f => f.name === "age") - age.typeOptions.minValue = 5 - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = 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 } = 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 => { - const createddate = find( - hierarchy.customerRecord.fields, - f => f.name === "createddate" - ) - createddate.typeOptions.maxValue = new Date() - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = 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 => { - const createddate = find( - hierarchy.customerRecord.fields, - f => f.name === "createddate" - ) - createddate.typeOptions.minValue = addHours(new Date(), 1) - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = 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 => { - 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 } = 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 => { - 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() - }) -}) diff --git a/packages/core/test/specHelpers.js b/packages/core/test/specHelpers.js deleted file mode 100644 index 7788873517..0000000000 --- a/packages/core/test/specHelpers.js +++ /dev/null @@ -1,648 +0,0 @@ -import path from "path" -import { - getRecordApi, - getCollectionApi, - getIndexApi, - getActionsApi, -} from "../src" -import couchDb, { getTestDb } from "./couchDb" -import { setupDatastore } from "../src/appInitialise" -import { - configFolder, - fieldDefinitions, - templateDefinitions, - joinKey, - isSomething, - crypto as nodeCrypto, -} from "../src/common" -import { getNewIndexTemplate } from "../src/templateApi/createNodes" -import { indexTypes } from "../src/templateApi/indexes" -import getTemplateApi from "../src/templateApi" -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 { setCleanupFunc } from "../src/transactions/setCleanupFunc" -import { permission } from "../src/authApi/permissions" -import { generateFullPermissions } from "../src/authApi/generateFullPermissions" -import { initialiseData } from "../src/appInitialise/initialiseData" - -export const testFileArea = testNameArea => - path.join("test", "fs_test_area", testNameArea) -export const testConfigFolder = testAreaName => - path.join(testFileArea(testAreaName), configFolder) -export const testFieldDefinitionsPath = testAreaName => - path.join(testFileArea(testAreaName), fieldDefinitions) -export const testTemplatesPath = testAreaName => - path.join(testFileArea(testAreaName), templateDefinitions) - -export const getMemoryStore = async () => - setupDatastore(couchDb(await getTestDb())) - -export const getMemoryTemplateApi = async store => { - const app = { - datastore: store || (await 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) setCleanupFunc(app, async () => { }) - else setCleanupFunc(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(app) - recordapi._storeHandle = app.datastore - return recordapi -} - -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.getNewModelTemplate(root, "customer") - customerRecord.collectionName = "customers" - findCollectionDefaultIndex(customerRecord).map = - "return {surname:record.surname, isalive:record.isalive, partner:record.partner};" - - const partnerRecord = templateApi.getNewModelTemplate(root, "partner") - partnerRecord.collectionName = "partners" - - const partnerInvoiceRecord = templateApi.getNewModelTemplate( - partnerRecord, - "invoice" - ) - partnerInvoiceRecord.collectionName = "invoices" - findCollectionDefaultIndex(partnerInvoiceRecord).name = - "partnerInvoices_index" - - const invoiceRecord = templateApi.getNewModelTemplate( - customerRecord, - "invoice" - ) - invoiceRecord.collectionName = "invoices" - findCollectionDefaultIndex(invoiceRecord).map = - "return {createdDate: record.createdDate, totalIncVat: record.totalIncVat};" - - const chargeRecord = templateApi.getNewModelTemplate(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, phone:record.phone};" - partnersReferenceIndex.allowedModelNodeIds = [partnerRecord.nodeId] - - const partnerCustomersReverseIndex = templateApi.getNewIndexTemplate( - partnerRecord, - indexTypes.reference - ) - partnerCustomersReverseIndex.name = "partnerCustomers" - partnerCustomersReverseIndex.map = "return {...record};" - partnerCustomersReverseIndex.filter = "record.isalive === true" - partnerCustomersReverseIndex.allowedModelNodeIds = [customerRecord.nodeId] - hierarchy.partnerCustomersReverseIndex = partnerCustomersReverseIndex - - newCustomerField("surname", "string") - newCustomerField("isalive", "bool", "true") - newCustomerField("createddate", "datetime") - newCustomerField("age", "number") - newCustomerField("profilepic", "file") - 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.allowedModelNodeIds = [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") - newPartnerField("phone", "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") - - 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.allowedModelNodeIds = [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.allowedModelNodeIds = [customerRecord.nodeId] - - 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.allowedModelNodeIds = [customerRecord.nodeId] - - findCollectionDefaultIndex(invoiceRecord).allowedModelNodeIds = [ - invoiceRecord.nodeId, - ] - findCollectionDefaultIndex(customerRecord).allowedModelNodeIds = [ - customerRecord.nodeId, - ] - findCollectionDefaultIndex(partnerRecord).allowedModelNodeIds = [ - partnerRecord.nodeId, - ] - findIndex(partnerRecord, "partnerInvoices_index").allowedModelNodeIds = [ - partnerInvoiceRecord.nodeId, - ] - findCollectionDefaultIndex(chargeRecord).allowedModelNodeIds = [ - chargeRecord.nodeId, - ] - - const customerInvoicesIndex = getNewIndexTemplate(root) - customerInvoicesIndex.name = "customer_invoices" - customerInvoicesIndex.map = "return record;" - customerInvoicesIndex.filter = "record.type === 'invoice'" - customerInvoicesIndex.allowedModelNodeIds = [invoiceRecord.nodeId] - - const outstandingInvoicesIndex = getNewIndexTemplate(root) - outstandingInvoicesIndex.name = "Outstanding Invoices" - outstandingInvoicesIndex.filter = - "record.type === 'invoice' && record.paidAmount < record.totalIncVat" - outstandingInvoicesIndex.map = "return {...record};" - outstandingInvoicesIndex.allowedModelNodeIds = [ - 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.allowedModelNodeIds = [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.allowedModelNodeIds = [ - 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 } = await 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 -} - -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 -} diff --git a/packages/core/test/support/jasmine.json b/packages/core/test/support/jasmine.json deleted file mode 100644 index 89452f1277..0000000000 --- a/packages/core/test/support/jasmine.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "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 deleted file mode 100644 index cda6fce6f8..0000000000 --- a/packages/core/test/templateApi.actionsValidation.spec.js +++ /dev/null @@ -1,110 +0,0 @@ -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.canDelete.spec.js b/packages/core/test/templateApi.canDelete.spec.js deleted file mode 100644 index 468e8c1ea9..0000000000 --- a/packages/core/test/templateApi.canDelete.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import { - setupApphierarchy, - basicAppHierarchyCreator_WithFields, - stubEventHandler, - basicAppHierarchyCreator_WithFields_AndIndexes, -} from "./specHelpers" -import { canDeleteIndex } from "../src/templateApi/canDeleteIndex" -import { canDeleteModel } from "../src/templateApi/canDeleteModel" - -describe("canDeleteIndex", () => { - it("should return no errors if deltion is valid", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - const partnerIndex = appHierarchy.root.indexes.find(i => i.name === "partner_index") - - const result = canDeleteIndex(partnerIndex) - - expect(result.canDelete).toBe(true) - expect(result.errors).toEqual([]) - }) - - it("should return errors if index is a lookup for a reference field", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - const customerIndex = appHierarchy.root.indexes.find(i => i.name === "customer_index") - - const result = canDeleteIndex(customerIndex) - - expect(result.canDelete).toBe(false) - expect(result.errors.length).toBe(1) - }) - - it("should return errors if index is a manyToOne index for a reference field", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - const referredToCustomersIndex = appHierarchy.customerRecord.indexes.find(i => i.name === "referredToCustomers") - - const result = canDeleteIndex(referredToCustomersIndex) - - expect(result.canDelete).toBe(false) - expect(result.errors.length).toBe(1) - }) -}) - - -describe("canDeleteModel", () => { - it("should return no errors when deletion is valid", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - appHierarchy.root.indexes = appHierarchy.root.indexes.filter(i => !i.allowedModelNodeIds.includes(appHierarchy.customerRecord.nodeId)) - const result = canDeleteModel(appHierarchy.customerRecord) - - expect(result.canDelete).toBe(true) - expect(result.errors).toEqual([]) - }) - - it("should return errors when record is referenced by hierarchal index", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields - ) - - const result = canDeleteModel(appHierarchy.customerRecord) - - expect(result.canDelete).toBe(false) - expect(result.errors.some(e => e.includes("customer_index"))).toBe(true) - }) - - it("should return errors when record has a child which cannot be deleted", async () => { - const { appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields_AndIndexes - ) - - const result = canDeleteModel(appHierarchy.customerRecord) - - expect(result.canDelete).toBe(false) - expect(result.errors.some(e => e.includes("Outstanding Invoices"))).toBe(true) - }) -}) \ No newline at end of file diff --git a/packages/core/test/templateApi.constructHeirarchy.spec.js b/packages/core/test/templateApi.constructHeirarchy.spec.js deleted file mode 100644 index 809b8ee945..0000000000 --- a/packages/core/test/templateApi.constructHeirarchy.spec.js +++ /dev/null @@ -1,184 +0,0 @@ -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([]) - expect(root.nodeName()).toBe("/") - }) - - it("> getNewModelTemplate > should be initialise with correct members", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const record = templateApi.getNewModelTemplate(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(record.nodeId.toString()) - expect(record.estimatedRecordCount).toBe(1000000) - 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.nodeName()).toBe(`/${record.name}`) - 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("> getNewModelTemplate > 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("> getNewModelTemplate > should add itself to parent records's children", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const parentRecord = templateApi.getNewModelTemplate(root) - const record = templateApi.getNewModelTemplate(parentRecord) - expect(parentRecord.children.length).toBe(1) - expect(parentRecord.children[0]).toBe(record) - }) - - it("> getNewModelTemplate > child should get correct nodeName ", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const parentRecord = templateApi.getNewModelTemplate(root) - parentRecord.name = "parent" - const record = templateApi.getNewModelTemplate(parentRecord) - record.name = "child" - expect(record.nodeName()).toBe(`/${parentRecord.name}/${record.name}`) - }) - - it("> getNewModelTemplate > should add itself to parents's default index allowedNodeIds", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const parentRecord = templateApi.getNewModelTemplate(root) - const record = templateApi.getNewModelTemplate(parentRecord) - expect(root.indexes[0].allowedModelNodeIds).toEqual([parentRecord.nodeId]) - expect(parentRecord.indexes[0].allowedModelNodeIds).toEqual([ - record.nodeId, - ]) - }) - - it("> getNewModelTemplate > should add itself to root's children", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const record = templateApi.getNewModelTemplate(root) - expect(root.children.length).toBe(1) - expect(root.children[0]).toBe(record) - }) - - it("> getNewModelTemplate > should have dynamic pathRegx if parent is record", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const parent = templateApi.getNewModelTemplate(root) - parent.collectionName = "customers" - const record = templateApi.getNewModelTemplate(parent) - record.name = "child" - expect(record.pathRegx().startsWith("/customers")).toBe(true) - expect(record.pathRegx().includes("[")).toBe(true) - }) - - it("> getNewModelTemplate > should add default index", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const record = templateApi.getNewModelTemplate(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.getNewModelTemplate(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.getNewModelTemplate()).toThrow( - errors.allNonRootNodesMustHaveParent - ) - }) - - it("> adding node > should just add one (bugfix)", async () => { - const { templateApi } = await getMemoryTemplateApi() - const root = templateApi.getNewRootLevel() - const parent = templateApi.getNewModelTemplate(root) - templateApi.getNewModelTemplate(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.getNewModelTemplate(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) - }) -}) diff --git a/packages/core/test/templateApi.diffHierarchy.spec.js b/packages/core/test/templateApi.diffHierarchy.spec.js deleted file mode 100644 index 3fa2d177e8..0000000000 --- a/packages/core/test/templateApi.diffHierarchy.spec.js +++ /dev/null @@ -1,269 +0,0 @@ -import { setup } from "./upgradeDataSetup" -import { diffHierarchy, HierarchyChangeTypes } from "../src/templateApi/diffHierarchy" - -describe("diffHierarchy", () => { - - it("should not show any changes, when hierarchy is unchanged", async () => { - const oldHierarchy = (await setup()).root; - const newHierarchy = (await setup()).root; - const diff = diffHierarchy(oldHierarchy, newHierarchy) - expect(diff).toEqual([]) - }) - - it("should detect root record created", async () => { - const oldHierarchy = (await setup()).root; - const newSetup = await setup() - const opportunity = newSetup.templateApi.getNewModelTemplate(newSetup.root, "opportunity", false) - const diff = diffHierarchy(oldHierarchy, newSetup.root) - expect(diff).toEqual([{ - newNode: opportunity, - oldNode: null, - type: HierarchyChangeTypes.recordCreated - }]) - }) - - it("should only detect root record, when newly created root record has children ", async () => { - const oldHierarchy = (await setup()).root; - const newSetup = await setup() - const opportunity = newSetup.templateApi.getNewModelTemplate(newSetup.root, "opportunity", false) - newSetup.templateApi.getNewModelTemplate(opportunity, "invoice", true) - const diff = diffHierarchy(oldHierarchy, newSetup.root) - expect(diff).toEqual([{ - newNode: opportunity, - oldNode: null, - type: HierarchyChangeTypes.recordCreated - }]) - }) - - it("should detect child record created", async () => { - const oldHierarchy = (await setup()).root; - const newSetup = await setup() - const opportunity = newSetup.templateApi.getNewModelTemplate(newSetup.contact, "opportunity", false) - const diff = diffHierarchy(oldHierarchy, newSetup.root) - expect(diff).toEqual([{ - newNode: opportunity, - oldNode: null, - type: HierarchyChangeTypes.recordCreated - }]) - }) - - it("should detect root record deleted", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.root.children = newSetup.root.children.filter(n => n.name !== "contact") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: null, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordDeleted - }]) - }) - - it("should detect child record deleted", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.contact.children = newSetup.contact.children.filter(n => n.name !== "deal") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: null, - oldNode: oldSetup.deal, - type: HierarchyChangeTypes.recordDeleted - }]) - }) - - it("should detect root record renamed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.contact.collectionKey = "CONTACTS" - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.contact, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordRenamed - }]) - }) - - it("should detect child record renamed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.deal.collectionKey = "CONTACTS" - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.deal, - oldNode: oldSetup.deal, - type: HierarchyChangeTypes.recordRenamed - }]) - }) - - it("should detect root record field removed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.contact.fields = newSetup.contact.fields.filter(f => f.name !== "name") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.contact, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordFieldsChanged - }]) - }) - - it("should detect child record field removed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.deal.fields = newSetup.deal.fields.filter(f => f.name !== "name") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.deal, - oldNode: oldSetup.deal, - type: HierarchyChangeTypes.recordFieldsChanged - }]) - }) - - it("should detect record field added", async () => { - const oldSetup = await setup() - const newSetup = await setup() - const notesField = newSetup.templateApi.getNewField("string") - notesField.name = "notes" - newSetup.templateApi.addField(newSetup.contact, notesField) - - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.contact, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordFieldsChanged - }]) - }) - - it("should detect 1 record field added and 1 removed (total no. fields unchanged)", async () => { - const oldSetup = await setup() - const newSetup = await setup() - const notesField = newSetup.templateApi.getNewField("string") - notesField.name = "notes" - newSetup.templateApi.addField(newSetup.contact, notesField) - newSetup.contact.fields = newSetup.contact.fields.filter(f => f.name !== "name") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.contact, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordFieldsChanged - }]) - }) - - it("should detect root record estimated record count changed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.contact.estimatedRecordCount = 987 - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.contact, - oldNode: oldSetup.contact, - type: HierarchyChangeTypes.recordEstimatedRecordTypeChanged - }]) - }) - - it("should detect root record estimated record count changed", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.deal.estimatedRecordCount = 987 - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup.deal, - oldNode: oldSetup.deal, - type: HierarchyChangeTypes.recordEstimatedRecordTypeChanged - }]) - }) - - it("should detect root index created", async () => { - const oldHierarchy = (await setup()).root - const newSetup = await setup() - const all_deals = newSetup.templateApi.getNewIndexTemplate(newSetup.root) - const diff = diffHierarchy(oldHierarchy, newSetup.root) - expect(diff).toEqual([{ - newNode: all_deals, - oldNode: null, - type: HierarchyChangeTypes.indexCreated - }]) - }) - - it("should detect child index created", async () => { - const oldHierarchy = (await setup()).root - const newSetup = await setup() - const all_deals = newSetup.templateApi.getNewIndexTemplate(newSetup.contact) - const diff = diffHierarchy(oldHierarchy, newSetup.root) - expect(diff).toEqual([{ - newNode: all_deals, - oldNode: null, - type: HierarchyChangeTypes.indexCreated - }]) - }) - - it("should detect root index deleted", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.root.indexes = newSetup.root.indexes.filter(i => i.name !== "contact_index") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: null, - oldNode: oldSetup.root.indexes.find(i => i.name === "contact_index"), - type: HierarchyChangeTypes.indexDeleted - }]) - }) - - it("should detect child index deleted", async () => { - const oldSetup = await setup() - const newSetup = await setup() - newSetup.contact.indexes = newSetup.contact.indexes.filter(i => i.name !== "deal_index") - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: null, - oldNode: oldSetup.contact.indexes.find(i => i.name === "deal_index"), - type: HierarchyChangeTypes.indexDeleted - }]) - }) - - const testIndexChanged = (parent, makechange) => async () => { - const oldSetup = await setup() - const newSetup = await setup() - makechange(newSetup) - const diff = diffHierarchy(oldSetup.root, newSetup.root) - expect(diff).toEqual([{ - newNode: newSetup[parent].indexes[0], - oldNode: oldSetup[parent].indexes[0], - type: HierarchyChangeTypes.indexChanged - }]) - } - - it("should detect root index map changed", testIndexChanged("root", newSetup => { - newSetup.root.indexes[0].map = "new" - })) - - it("should detect root index filter changed", testIndexChanged("root", newSetup => { - newSetup.root.indexes[0].filter = "new" - })) - - it("should detect root index shardName changed", testIndexChanged("root", newSetup => { - newSetup.root.indexes[0].getShardName = "new" - })) - - it("should detect root index allowedRecordIds changed", testIndexChanged("root", newSetup => { - newSetup.root.indexes[0].allowedModelNodeIds.push(3) - })) - - it("should detect child index allowedRecordIds changed", testIndexChanged("contact", newSetup => { - newSetup.contact.indexes[0].allowedModelNodeIds.push(3) - })) - - it("should detect child index map changed", testIndexChanged("contact", newSetup => { - newSetup.contact.indexes[0].map = "new" - })) - - it("should detect child index filter changed", testIndexChanged("contact", newSetup => { - newSetup.contact.indexes[0].filter = "new" - })) - - it("should detect child index shardName changed", testIndexChanged("contact", newSetup => { - newSetup.contact.indexes[0].getShardName = "new" - })) - -}) - diff --git a/packages/core/test/templateApi.fields.spec.js b/packages/core/test/templateApi.fields.spec.js deleted file mode 100644 index 05612de9ac..0000000000 --- a/packages/core/test/templateApi.fields.spec.js +++ /dev/null @@ -1,124 +0,0 @@ -import { isDefined, join, fieldDefinitions, $ } from "../src/common" -import { getMemoryTemplateApi } from "./specHelpers" -import { fieldErrors } from "../src/templateApi/fields" - -const getRecordTemplate = templateApi => - $(templateApi.getNewRootLevel(), [templateApi.getNewModelTemplate]) - -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 deleted file mode 100644 index c1a914a339..0000000000 --- a/packages/core/test/templateApi.heirarchyValidation.spec.js +++ /dev/null @@ -1,513 +0,0 @@ -import { validateAll } from "../src/templateApi/validate" -import createNodes from "../src/templateApi/createNodes" -import { some } from "lodash" -import { getNewField, addField } from "../src/templateApi/fields" -import { - 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.getNewModelTemplate(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.getNewModelTemplate(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 = "hello" - 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 = "hello" - 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 deleted file mode 100644 index ca86d95182..0000000000 --- a/packages/core/test/templateApi.loadSaveHeirarchy.spec.js +++ /dev/null @@ -1,181 +0,0 @@ -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.getNewModelTemplate(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) - }) -}) diff --git a/packages/core/test/templateApi.types.spec.js b/packages/core/test/templateApi.types.spec.js deleted file mode 100644 index 222bd38116..0000000000 --- a/packages/core/test/templateApi.types.spec.js +++ /dev/null @@ -1,285 +0,0 @@ -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 } - )() - }) -}) diff --git a/packages/core/test/templateApi.upgradeData.spec.js b/packages/core/test/templateApi.upgradeData.spec.js deleted file mode 100644 index d6d172c9e0..0000000000 --- a/packages/core/test/templateApi.upgradeData.spec.js +++ /dev/null @@ -1,327 +0,0 @@ -import { - getRecordApiFromTemplateApi, - getIndexApiFromTemplateApi, -} from "./specHelpers" -import { upgradeData } from "../src/templateApi/upgradeData" -import { setup } from "./upgradeDataSetup" -import { $, splitKey } from "../src/common" -import { keys, filter } from "lodash/fp" -import { _listItems } from "../src/indexApi/listItems" -import { _save } from "../src/recordApi/save" -import { _getNew } from "../src/recordApi/getNew" - -describe("upgradeData", () => { - - it("should delete all records and child records, when root record node deleted", async () => { - const { oldSetup, newSetup, recordApi } = await configure() - newSetup.root.children = newSetup.root.children.filter(n => n.name !== "contact") - - await upgradeData(oldSetup.app)(newSetup.root) - - const remainingKeys = $(recordApi._storeHandle.data, [ - keys, - filter(k => splitKey(k)[0] === "contacts"), - ]) - - expect(remainingKeys.length).toBe(0) - - }) - - it("should not delete other root record types, when root record node deleted", async () => { - const { oldSetup, newSetup, recordApi } = await configure() - newSetup.root.children = newSetup.root.children.filter(n => n.name !== "contact") - - await upgradeData(oldSetup.app)(newSetup.root) - - const remainingKeys = $(recordApi._storeHandle.data, [ - keys, - filter(k => splitKey(k)[0] === "leads"), - ]) - - expect(remainingKeys.length > 0).toBe(true) - - }) - - it("should delete all child records, when child record node deleted", async () => { - const { oldSetup, newSetup, recordApi } = await configure() - newSetup.contact.children = newSetup.contact.children.filter(n => n.name !== "deal") - - const startingKeys = $(recordApi._storeHandle.data, [ - keys, - filter(k => k.includes("/deals/")), - ]) - - expect(startingKeys.length > 0).toBe(true) - - await upgradeData(oldSetup.app)(newSetup.root) - - const remainingKeys = $(recordApi._storeHandle.data, [ - keys, - filter(k => k.includes("/deals/")), - ]) - - expect(remainingKeys.length).toBe(0) - }) - - it("should build a new root index", async () => { - const { oldSetup, newSetup } = await configure() - const newIndex = newSetup.templateApi.getNewIndexTemplate(newSetup.root) - newIndex.name = "more_contacts" - newIndex.allowedModelNodeIds = [newSetup.contact.nodeId] - - await upgradeData(oldSetup.app)(newSetup.root) - - const itemsInNewIndex = await _listItems(newSetup.app, "/more_contacts") - - expect(itemsInNewIndex.length).toBe(2) - }) - - it("should update a root index", async () => { - const { oldSetup, newSetup } = await configure() - const contact_index = indexByName(newSetup.root, "contact_index") - contact_index.filter = "record.name === 'bobby'" - - await upgradeData(oldSetup.app)(newSetup.root) - - const itemsInNewIndex = await _listItems(newSetup.app, "/contact_index") - - expect(itemsInNewIndex.length).toBe(1) - }) - - it("should delete a root index", async () => { - const { oldSetup, newSetup } = await configure() - - // no exception - await _listItems(newSetup.app, "/contact_index") - - newSetup.root.indexes = newSetup.root.indexes.filter(i => i.name !== "contact_index") - - await upgradeData(oldSetup.app)(newSetup.root) - - let er - try { - await _listItems(newSetup.app, "/contact_index") - } catch (e) { - er = e - } - - expect(er).toBeDefined() - }) - - it("should build a new child index", async () => { - const { oldSetup, newSetup, records } = await configure() - const newIndex = newSetup.templateApi.getNewIndexTemplate(newSetup.contact) - newIndex.name = "more_deals" - newIndex.allowedModelNodeIds = [newSetup.deal.nodeId] - - await upgradeData(oldSetup.app)(newSetup.root) - - const itemsInNewIndex = await _listItems(newSetup.app, `${records.contact1.key}/more_deals`) - - expect(itemsInNewIndex.length).toBe(2) - }) - - it("should update a child index", async () => { - const { oldSetup, newSetup, records } = await configure() - const deal_index = indexByName(newSetup.contact, "deal_index") - deal_index.filter = "record.status === 'new'" - - let itemsInIndex = await _listItems(newSetup.app, `${records.contact1.key}/deal_index`) - expect(itemsInIndex.length).toBe(2) - - await upgradeData(oldSetup.app)(newSetup.root) - - itemsInIndex = await _listItems(newSetup.app, `${records.contact1.key}/deal_index`) - expect(itemsInIndex.length).toBe(1) - }) - - it("should delete a child index", async () => { - const { oldSetup, newSetup, records } = await configure() - - // no exception - await _listItems(newSetup.app, `${records.contact1.key}/deal_index`) - - newSetup.contact.indexes = newSetup.contact.indexes.filter(i => i.name !== "deal_index") - - await upgradeData(oldSetup.app)(newSetup.root) - - let er - try { - await _listItems(newSetup.app, `${records.contact1.key}/deal_index`) - } catch (e) { - er = e - } - - expect(er).toBeDefined() - }) - - it("should build a new reference index", async () => { - const { oldSetup, newSetup, records, recordApi } = await configure() - const newIndex = newSetup.templateApi.getNewIndexTemplate(newSetup.lead) - newIndex.name = "contact_leads" - newIndex.allowedModelNodeIds = [newSetup.lead.nodeId] - newIndex.indexType = "reference" - - const leadField = newSetup.templateApi.getNewField("string") - leadField.name = "lead" - leadField.type = "reference" - leadField.typeOptions = { - reverseIndexNodeKeys: [newIndex.nodeKey()], - indexNodeKey: "/lead_index", - displayValue: "name" - } - - newSetup.templateApi.addField(newSetup.contact, leadField) - - await upgradeData(oldSetup.app)(newSetup.root) - - const indexKey = `${records.lead1.key}/contact_leads` - - let itemsInNewIndex = await _listItems(newSetup.app, indexKey) - - expect(itemsInNewIndex.length).toBe(0) - - records.contact1.lead = records.lead1 - records.contact1.isNew = false - - await _save(newSetup.app, records.contact1) - - itemsInNewIndex = await _listItems(newSetup.app, indexKey) - - expect(itemsInNewIndex.length).toBe(1) - - }) - - it("should initialise a new root record", async () => { - const { oldSetup, newSetup } = await configure() - const invoice = newSetup.templateApi.getNewModelTemplate(newSetup.root, "invoice", true) - invoice.collectionName = "invoices" - - const nameField = newSetup.templateApi.getNewField("string") - nameField.name = "name" - newSetup.templateApi.addField(invoice, nameField) - - await upgradeData(oldSetup.app)(newSetup.root) - - let itemsInNewIndex = await _listItems(newSetup.app, "/invoice_index") - - expect(itemsInNewIndex.length).toBe(0) - - const newInvoice = _getNew(invoice, "/invoices") - await _save(newSetup.app, newInvoice) - - itemsInNewIndex = await _listItems(newSetup.app, "/invoice_index") - - expect(itemsInNewIndex.length).toBe(1) - }) - - it("should initialise a new child record", async () => { - const { oldSetup, newSetup, records } = await configure() - const invoice = newSetup.templateApi.getNewModelTemplate(newSetup.contact, "invoice", true) - invoice.collectionName = "invoices" - - const nameField = newSetup.templateApi.getNewField("string") - nameField.name = "name" - newSetup.templateApi.addField(invoice, nameField) - - await upgradeData(oldSetup.app)(newSetup.root) - - let itemsInNewIndex = await _listItems(newSetup.app, `${records.contact1.key}/invoice_index`) - - expect(itemsInNewIndex.length).toBe(0) - - const newInvoice = _getNew(invoice, `${records.contact1.key}/invoices`) - await _save(newSetup.app, newInvoice) - - itemsInNewIndex = await _listItems(newSetup.app, `${records.contact1.key}/invoice_index`) - - expect(itemsInNewIndex.length).toBe(1) - }) - -}) - -it("should rebuild affected index when field is removed", async () => { - const { oldSetup, newSetup, records } = await configure() - newSetup.contact.fields = newSetup.contact.fields.filter(f => f.name !== "status") - - let itemsInIndex = await _listItems(oldSetup.app, "/contact_index") - expect(itemsInIndex.length).toBe(2) - expect(itemsInIndex[0].status).toBeDefined() - expect(itemsInIndex[0].status).toBe(records.contact1.status) - - await upgradeData(oldSetup.app)(newSetup.root) - - itemsInIndex = await _listItems(newSetup.app, "/contact_index") - expect(itemsInIndex.length).toBe(2) - expect(itemsInIndex[0].status).toBeUndefined() -}) - -it("should rebuild affected index when field is added", async () => { - const { oldSetup, newSetup, records } = await configure() - - const aliveField = newSetup.templateApi.getNewField("string") - aliveField.name = "isalive" - newSetup.templateApi.addField(newSetup.contact, aliveField) - - let itemsInIndex = await _listItems(oldSetup.app, "/contact_index") - expect(itemsInIndex.length).toBe(2) - expect(itemsInIndex[0].isalive).toBeUndefined() - - await upgradeData(oldSetup.app)(newSetup.root) - - itemsInIndex = await _listItems(newSetup.app, "/contact_index") - expect(itemsInIndex.length).toBe(2) - expect(itemsInIndex[0].isalive).toBe(null) -}) - -const configure = async () => { - const oldSetup = await setup() - - const recordApi = await getRecordApiFromTemplateApi(oldSetup.templateApi) - const indexApi = await getIndexApiFromTemplateApi(oldSetup.templateApi) - - const newSetup = await setup(oldSetup.store) - - const records = await createSomeRecords(recordApi) - - return { oldSetup, newSetup, recordApi, records, indexApi } -} - -const createSomeRecords = async recordApi => { - const contact1 = recordApi.getNew("/contacts", "contact") - contact1.name = "bobby" - contact1.status = "New" - const contact2 = recordApi.getNew("/contacts", "contact") - contact2.name = "poppy" - contact2.status = "Complete" - - await recordApi.save(contact1) - await recordApi.save(contact2) - - const deal1 = recordApi.getNew(`${contact1.key}/deals`, "deal") - deal1.name = "big mad deal" - deal1.status = "new" - const deal2 = recordApi.getNew(`${contact1.key}/deals`, "deal") - deal2.name = "smaller deal" - deal2.status = "old" - const deal3 = recordApi.getNew(`${contact2.key}/deals`, "deal") - deal3.name = "ok deal" - deal3.status = "new" - - await recordApi.save(deal1) - await recordApi.save(deal2) - await recordApi.save(deal3) - - const lead1 = recordApi.getNew("/leads", "lead") - lead1.name = "big new lead" - - await recordApi.save(lead1) - - - - return { - contact1, contact2, deal1, deal2, deal3, lead1, - } -} - -const indexByName = (parent, name) => parent.indexes.find(i => i.name === name) \ No newline at end of file diff --git a/packages/core/test/upgradeDataSetup.js b/packages/core/test/upgradeDataSetup.js deleted file mode 100644 index 84fbc14ee5..0000000000 --- a/packages/core/test/upgradeDataSetup.js +++ /dev/null @@ -1,52 +0,0 @@ -import { getMemoryTemplateApi, appFromTempalteApi } from "./specHelpers" -import { getFlattenedHierarchy } from "../src/templateApi/hierarchy" -import { initialiseData } from "../src/appInitialise/initialiseData" - -export const setup = async store => { - const { templateApi } = await getMemoryTemplateApi(store) - const root = templateApi.getNewRootLevel() - const contact = templateApi.getNewModelTemplate(root, "contact", true) - contact.collectionName = "contacts" - - const nameField = templateApi.getNewField("string") - nameField.name = "name" - const statusField = templateApi.getNewField("string") - statusField.name = "status" - - templateApi.addField(contact, nameField) - templateApi.addField(contact, statusField) - - const lead = templateApi.getNewModelTemplate(root, "lead", true) - lead.collectionName = "leads" - const deal = templateApi.getNewModelTemplate(contact, "deal", true) - deal.collectionName = "deals" - - templateApi.addField(deal, { ...nameField }) - templateApi.addField(deal, { ...statusField }) - - templateApi.addField(lead, { ...nameField }) - - getFlattenedHierarchy(root) - - if (!store) - await initialiseData(templateApi._storeHandle, { - hierarchy: root, - actions: [], - triggers: [], - }) - const app = await appFromTempalteApi(templateApi) - app.hierarchy = root - - return { - root, - contact, - lead, - app, - deal, - templateApi, - store: templateApi._storeHandle, - all_contacts: root.indexes[0], - all_leads: root.indexes[1], - deals_for_contacts: contact.indexes[0], - } -} diff --git a/packages/core/yarn.lock b/packages/core/yarn.lock deleted file mode 100644 index 4a264485bf..0000000000 --- a/packages/core/yarn.lock +++ /dev/null @@ -1,5725 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/cli@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.5.5.tgz#bdb6d9169e93e241a08f5f7b0265195bf38ef5ec" - integrity sha512-UHI+7pHv/tk9g6WXQKYz+kmXTI77YtuY3vqC59KIqcoWEjsJJSG6rAxKaLsgj3LDyadsPrCB929gVOKM6Hui0w== - dependencies: - commander "^2.8.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.0.0" - lodash "^4.17.13" - 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", "@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" - -"@babel/core@^7.1.0", "@babel/core@^7.4.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" - integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helpers" "^7.5.5" - "@babel/parser" "^7.5.5" - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - convert-source-map "^1.1.0" - debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.4.0", "@babel/generator@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" - integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== - dependencies: - "@babel/types" "^7.5.5" - jsesc "^2.5.1" - lodash "^4.17.13" - 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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" - integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/types" "^7.5.5" - lodash "^4.17.13" - -"@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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" - integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA== - dependencies: - "@babel/types" "^7.5.5" - -"@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.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" - integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw== - 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.5.5" - lodash "^4.17.13" - -"@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.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" - integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== - dependencies: - lodash "^4.17.13" - -"@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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" - integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.5.5" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - -"@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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" - integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g== - dependencies: - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - -"@babel/highlight@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" - integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" - integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== - -"@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-dynamic-import@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506" - integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" - integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== - 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-dynamic-import@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" - integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== - 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.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" - integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== - 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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce" - integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" - -"@babel/plugin-transform-classes@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" - integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-define-map" "^7.5.5" - "@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.5.5" - "@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.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" - integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ== - 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.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" - integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ== - 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.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" - integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-commonjs@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" - integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ== - dependencies: - "@babel/helper-module-transforms" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-systemjs@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" - integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg== - dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.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.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" - integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - -"@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.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" - integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== - 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.5.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a" - integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A== - 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-dynamic-import" "^7.5.0" - "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.5.5" - "@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-dynamic-import" "^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.5.0" - "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.5.5" - "@babel/plugin-transform-classes" "^7.5.5" - "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.5.0" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/plugin-transform-duplicate-keys" "^7.5.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.5.0" - "@babel/plugin-transform-modules-commonjs" "^7.5.0" - "@babel/plugin-transform-modules-systemjs" "^7.5.0" - "@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.5.5" - "@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.5.5" - 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.5.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" - integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== - 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.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" - integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.5.5" - "@babel/types" "^7.5.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@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", "@babel/types@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" - integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== - dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - 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", "@jest/console@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" - integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== - dependencies: - "@jest/source-map" "^24.9.0" - chalk "^2.0.1" - slash "^2.0.0" - -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== - dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" - -"@jest/environment@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== - dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - -"@jest/fake-timers@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" - integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== - dependencies: - "@jest/types" "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.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.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" - source-map "^0.6.0" - string-length "^2.0.0" - -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" - integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.1.15" - source-map "^0.6.0" - -"@jest/test-result@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" - integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== - dependencies: - "@jest/console" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/istanbul-lib-coverage" "^2.0.0" - -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== - dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^24.9.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.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" - pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" - source-map "^0.6.1" - write-file-atomic "2.4.1" - -"@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" - -"@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== - -"@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.7" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f" - integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw== - dependencies: - "@babel/types" "^7.3.0" - -"@types/caseless@*": - version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" - integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== - -"@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.7.2": - version "12.7.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" - integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== - -"@types/request@^2.48.4": - version "2.48.4" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e" - integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - -"@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/tough-cookie@*": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" - integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== - -"@types/yargs-parser@*": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.0.0.tgz#453743c5bbf9f1bed61d959baab5b06be029b2d0" - integrity sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw== - -"@types/yargs@^13.0.0": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.2.tgz#a64674fc0149574ecd90ba746e932b5a5f7b3653" - integrity sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ== - dependencies: - "@types/yargs-parser" "*" - -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.3" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.3.tgz#a86f75b69680b8780d30edd21eee4e0ea170c05e" - integrity sha512-vkR40VwS2SYO98AIeFvzWWh+xyc2qi9s7OoXSFEGIP/rOJKzjnhykaZJNnHdoq4BL2gGxI5EZOU16z896EYnOQ== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - -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: - version "6.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" - integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== - -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== - -ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - 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: - 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" - -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-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= - -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.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -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== - -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-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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== - dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.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-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== - dependencies: - object.assign "^4.1.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.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" - integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - 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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" - integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== - 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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" - integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== - dependencies: - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.9.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" - -bcryptjs@^2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" - integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= - -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== - -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-request@~0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" - integrity sha1-ns5bWsqJopkyJC4Yv5M975h2zBc= - -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, browserslist@^4.6.6: - version "4.6.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" - integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== - dependencies: - caniuse-lite "^1.0.30000984" - electron-to-chromium "^1.3.191" - node-releases "^1.1.25" - -bser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.0.tgz#65fc784bf7f87c009b973c12db6546902fa9c7b5" - integrity sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg== - 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, camelcase@^5.3.1: - 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.30000984: - version "1.0.30000989" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== - -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.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" - -chokidar@^2.0.4: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - 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.2" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== - -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" - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.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= - -cloudant-follow@^0.18.2: - version "0.18.2" - resolved "https://registry.yarnpkg.com/cloudant-follow/-/cloudant-follow-0.18.2.tgz#35dd7b29c5b9c58423d50691f848a990fbe2c88f" - integrity sha512-qu/AmKxDqJds+UmT77+0NbM7Yab2K3w0qSeJRzsq5dRWJTEJdWeb+XpG4OpKuTE9RKOa/Awn2gR3TTnvNr3TeA== - dependencies: - browser-request "~0.3.0" - debug "^4.0.1" - request "^2.88.0" - -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.8.1: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - -commander@~2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -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" - -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= - -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.2.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.2.1.tgz#0cbdbc2e386e8e00d3b85dc81c848effec5b8150" - integrity sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A== - dependencies: - browserslist "^4.6.6" - semver "^6.3.0" - -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.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== - dependencies: - cssom "0.3.x" - -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: - 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: - 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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== - -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" - -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.191: - version "1.3.241" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.241.tgz#859dc49ab7f90773ed698767372d384190f60cb1" - integrity sha512-Gb9E6nWZlbgjDDNe5cAvMJixtn79krNJ70EDpq/M10lkGo7PGtBUe7Y0CYVHsBScRwi6ybCS+YetXAN9ysAHDg== - -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: - 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" - -errs@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/errs/-/errs-0.3.2.tgz#798099b2dbd37ca2bc749e538a7c1307d0b50499" - integrity sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk= - -es-abstract@^1.5.1: - 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.12.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" - integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -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.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.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== - dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.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== - -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" - -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.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" - -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.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -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== - -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@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -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: - 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.2.2" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" - integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== - -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.7.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee" - integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg== - 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, har-validator@~5.1.3: - 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.8.4" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== - -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.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" - -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.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - 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== - -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" - -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.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-reference@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.3.tgz#e99059204b66fdbe09305cfca715a29caa5c8a51" - integrity sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw== - 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: - 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.2.6: - 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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== - dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" - -jest-cli@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== - dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - exit "^0.1.2" - import-local "^2.0.0" - is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" - -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" - glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" - -jest-diff@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== - dependencies: - detect-newline "^2.1.0" - -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== - dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" - -jest-environment-node@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" - integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - -jest-get-type@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" - integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== - -jest-haste-map@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" - integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== - dependencies: - "@jest/types" "^24.9.0" - anymatch "^2.0.0" - fb-watchman "^2.0.0" - graceful-fs "^4.1.15" - invariant "^2.2.4" - jest-serializer "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.9.0" - micromatch "^3.1.10" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^1.2.7" - -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - co "^4.6.0" - expect "^24.9.0" - is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" - -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== - dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== - dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-message-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" - integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" - integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== - dependencies: - "@jest/types" "^24.9.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, jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== - -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== - dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" - -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== - dependencies: - "@jest/types" "^24.9.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - source-map-support "^0.5.6" - throat "^4.0.0" - -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" - -jest-serializer@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" - integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== - -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" - -jest-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" - integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== - dependencies: - "@jest/console" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/source-map" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" - integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== - dependencies: - "@jest/types" "^24.9.0" - camelcase "^5.3.1" - chalk "^2.0.1" - jest-get-type "^24.9.0" - leven "^3.1.0" - pretty-format "^24.9.0" - -jest-watcher@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" - integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== - dependencies: - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - jest-util "^24.9.0" - string-length "^2.0.0" - -jest-worker@^24.6.0, jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== - dependencies: - merge-stream "^2.0.0" - supports-color "^6.1.0" - -jest@^24.8.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" - integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== - dependencies: - import-local "^2.0.0" - jest-cli "^24.9.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= - -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-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" - -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.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -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@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -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@^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.13, lodash@^4.17.4: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -loose-envify@^1.0.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.3" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.3.tgz#34b8d2a2c7fec9d9bdf9929a3fd81d271ef35be9" - integrity sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA== - 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-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" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -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" - -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.4.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.4.0.tgz#38f0af94f42fb6f34d3d7d82a90e2c99cd3ff485" - integrity sha512-6PmOuSP4NnZXzs2z6rbwzLJu/c5gdzYg1mRI/WIYdx45iiX7T+a4esOzavD6V/KmBzAaopFSTZPZcUx73bqKWA== - 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.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - 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== - -nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nano@^8.2.2: - version "8.2.2" - resolved "https://registry.yarnpkg.com/nano/-/nano-8.2.2.tgz#4fdd48965cece51892cf41e78d433d1b772e6e40" - integrity sha512-1/rAvpd1J0Os0SazgutWQBx2buAq3KwJpmdIylPDqOwy73iQeAhTSCq3uzbGzvcNNW16Vv/BLXkk+DYcdcH+aw== - dependencies: - "@types/request" "^2.48.4" - cloudant-follow "^0.18.2" - debug "^4.1.1" - errs "^0.3.2" - request "^2.88.0" - -nanoid@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.0.4.tgz#4889355c9ce8e24efad7c65945a4a2875ac3e8f4" - integrity sha512-sOJnBmY3TJQBVIBqKHoifuwygrocXg3NjS9rZSMnVl05XWSHK7Qxb177AIZQyMDjP86bz+yneozj/h9qsPLcCA== - -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-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.4.2: - version "5.4.3" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" - integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== - 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.25: - version "1.1.28" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.28.tgz#503c3c70d0e4732b84e7aaa2925fbdde10482d4a" - integrity sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g== - 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.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== - 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: - 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.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" - -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" - -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: - 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-tmpdir@^1.0.0: - 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-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-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.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - 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== - -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-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@^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@^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.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.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.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -prompts@^2.0.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35" - integrity sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.3" - -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.3.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd" - integrity sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag== - -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.4: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" - integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== - -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@^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@^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.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.4.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== - 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.1.0: - 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.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" - integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== - 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.12" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.12.tgz#28eaaa6e66eeb3527c15108a3ff740d9e574e420" - integrity sha512-TsXZ8+cv2uxMEkLfgwO0E068gsNMLfuYwMMhiUxf0Kw2Vcgzq93vgl6wIlIYuPmfMqMjfQ9zAporiozqCnwLuQ== - -regexpu-core@^4.5.4: - version "4.5.5" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411" - integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - 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" - -request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - 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.3" - 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.5.0" - 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-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.11.0, resolve@^1.11.1, resolve@^1.3.2, resolve@^1.8.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== - dependencies: - path-parse "^1.0.6" - -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.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - 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.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.0.2.tgz#61328f3a29945e2c35f2b2e824c18944fd88a54d" - integrity sha512-DxeR4QXTgTOFseYls1V7vgKbrSJmPYNdEMOs0OvH+7+89C3GiIonU9gFrE0u39Vv1KWm3wepq8KAvKugtoM2Zw== - dependencies: - estree-walker "^0.6.1" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" - rollup-pluginutils "^2.8.1" - -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.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" - integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== - dependencies: - "@types/resolve" "0.0.8" - builtin-modules "^3.1.0" - is-module "^1.0.0" - resolve "^1.11.1" - rollup-pluginutils "^2.8.1" - -rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.8.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@^1.12.0: - version "1.20.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.20.2.tgz#0e1be13cb5de244c9c463027092f7c93461558b9" - integrity sha512-pF4jFzNWMUuudIAeiTgOcSxn8XkBN2Y2/IwPR7iL/IZ8k9RwoLyp2QwNWiYT+HF537zwpmzZHTBYw345H9vq1A== - dependencies: - "@types/estree" "0.0.39" - "@types/node" "^12.7.2" - acorn "^7.0.0" - -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - -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-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.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -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@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - 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.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.3.tgz#98168d62b79e3a5e758e27ae63c4a053d748f4eb" - integrity sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg== - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - -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.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - 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.6" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" - integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg== - -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.5" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" - integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== - -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" - -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": - 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, string-width@^3.1.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.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.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, strip-ansi@^5.2.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: - 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.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -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" - -throat@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" - integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= - -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, tough-cookie@~2.5.0: - 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= - -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.7.7" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.7.tgz#21e52c7dccda80a53bf7cde69628a7e511aec9c9" - integrity sha512-FeSU+hi7ULYy6mn8PKio/tXsdSXN35lm4KgV2asx00kzrLU9Pi3oAslcJT70Jdj7PHX29gGUPOT6+lXGBbemhA== - dependencies: - commander "~2.20.3" - 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.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -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.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== - -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@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -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" - -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@^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@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" diff --git a/packages/server/api/controllers/auth.js b/packages/server/api/controllers/auth.js index 13ebbcfa5f..e2bb75e77c 100644 --- a/packages/server/api/controllers/auth.js +++ b/packages/server/api/controllers/auth.js @@ -38,12 +38,13 @@ exports.authenticate = async ctx => { instanceId: instanceId }; - const token = jwt.sign(payload, ctx.config.jwtSecret, { expiresIn: "1 day" }); + + const ONE_DAY_FROM_NOW = new Date(Date.now() + (24 * 3600)) - ctx.cookies.set('budibase:token', token); + ctx.cookies.set('budibase:token', token, { expires: ONE_DAY_FROM_NOW }); ctx.body = { token,