diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..ec6bedd2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,45 @@ +--- +name: 🐛 Bug report +about: Create a report to help us improve AgileTs +title: '' +labels: 'Type: Bug' +assignees: '' + +--- + +## 🐛 Bug report + +### 🤖 Current Behavior + + + +### 🎯 Expected behavior + + + +### 📄 Reproducible example + + + +### 💡 Suggested solution(s) + + + +### ➕ Additional notes + + + +### 💻 Your environment + + + +| Software | Version(s) | +| ----------------------| ---------- | +| TypeScript | +| npm/Yarn | +| NodeJs | +| @agile-ts/core | +| @agile-ts/react | +| @agile-ts/api | +| @agile-ts/multieditor | + diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..592b8985 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: 🔨 Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..ca1f6d36 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: 🆕 Feature request +about: Suggest an idea for this project +title: '' +labels: 'Type: Enhancement' +assignees: '' + +--- + +## 🆕 Feature Request + +### ❓ Is your feature request related to a problem? + + +### 📄 Describe the solution you'd like + + +### 📃 Describe alternatives you've considered + + +### ➕ Additional Notes + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4bd61b3e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ + + +## 📄 Description + + +## 🔴 Related Issue + + + +## 📃 Context + + + +## 🛠 How Has This Been Tested? + + + diff --git a/LICENCE b/LICENCE new file mode 100644 index 00000000..b93156fb --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present bennodev19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/AwesomeTSProject/__tests__/App-test.tsx b/examples/AwesomeTSProject/__tests__/App-test.tsx deleted file mode 100644 index 17847669..00000000 --- a/examples/AwesomeTSProject/__tests__/App-test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @format - */ - -import 'react-native'; -import React from 'react'; -import App from '../App'; - -// Note: test renderer must be required after react-native. -import renderer from 'react-test-renderer'; - -it('renders correctly', () => { - renderer.create(); -}); diff --git a/examples/react-typescript/.env b/examples/react-typescript/.env new file mode 100644 index 00000000..44c0ec83 --- /dev/null +++ b/examples/react-typescript/.env @@ -0,0 +1 @@ + SKIP_PREFLIGHT_CHECK=true \ No newline at end of file diff --git a/examples/react-typescript/package-lock.json b/examples/react-typescript/package-lock.json index 1cffff40..d50d0759 100644 --- a/examples/react-typescript/package-lock.json +++ b/examples/react-typescript/package-lock.json @@ -25,9 +25,9 @@ } }, "@babel/compat-data": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.1.tgz", - "integrity": "sha512-725AQupWJZ8ba0jbKceeFblZTY90McUBWMwHhkFQ9q1zKPJ95GUktljFcgcsIVwRnTnRKlcYzfiNImg5G9m6ZQ==" + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", + "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==" }, "@babel/core": { "version": "7.9.0", @@ -60,11 +60,11 @@ } }, "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "requires": { - "@babel/types": "^7.12.1", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -106,13 +106,13 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.1.tgz", - "integrity": "sha512-jtBEif7jsPwP27GPHs06v4WBV0KrE8a/P7n0N0sSvHn2hwUCYnolP/CLmz51IzAW4NlN+HuoBtb9QcwnRo9F/g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", "requires": { - "@babel/compat-data": "^7.12.1", + "@babel/compat-data": "^7.12.5", "@babel/helper-validator-option": "^7.12.1", - "browserslist": "^4.12.0", + "browserslist": "^4.14.5", "semver": "^5.5.0" }, "dependencies": { @@ -136,12 +136,11 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", - "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", + "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", "regexpu-core": "^4.7.1" } }, @@ -190,19 +189,19 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.7" } }, "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { @@ -222,11 +221,11 @@ } }, "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", + "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.7" } }, "@babel/helper-plugin-utils": { @@ -234,14 +233,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "requires": { - "lodash": "^4.17.19" - } - }, "@babel/helper-remap-async-to-generator": { "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", @@ -253,14 +244,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "requires": { "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/helper-simple-access": { @@ -309,13 +300,13 @@ } }, "@babel/helpers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.1.tgz", - "integrity": "sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { @@ -329,9 +320,9 @@ } }, "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==" + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", + "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.12.1", @@ -408,9 +399,9 @@ } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz", - "integrity": "sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", + "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -436,9 +427,9 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", - "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", + "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", @@ -824,22 +815,22 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.1.tgz", - "integrity": "sha512-RmKejwnT0T0QzQUzcbP5p1VWlpnP8QHtdhEtLG55ZDQnJNalbF3eeDyu3dnGKvGzFIQiBzFhBYTwvv435p9Xpw==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.7.tgz", + "integrity": "sha512-YFlTi6MEsclFAPIDNZYiCRbneg1MFGao9pPG9uD5htwE0vDbPaMUMeYd6itWjw7K4kro4UbdQf3ljmFl9y48dQ==", "requires": { "@babel/helper-builder-react-jsx": "^7.10.4", - "@babel/helper-builder-react-jsx-experimental": "^7.12.1", + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-jsx": "^7.12.1" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.1.tgz", - "integrity": "sha512-IilcGWdN1yNgEGOrB96jbTplRh+V2Pz1EoEwsKsHfX1a/L40cUYuD71Zepa7C+ujv7kJIxnDftWeZbKNEqZjCQ==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.7.tgz", + "integrity": "sha512-Rs3ETtMtR3VLXFeYRChle5SsP/P9Jp/6dsewBQfokDSzKJThlsuFcnzLTDRALiUmTC48ej19YD9uN1mupEeEDg==", "requires": { - "@babel/helper-builder-react-jsx-experimental": "^7.12.1", + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-jsx": "^7.12.1" } @@ -921,12 +912,11 @@ } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", - "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", + "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-template-literals": { @@ -973,13 +963,13 @@ } }, "@babel/preset-env": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", - "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.7.tgz", + "integrity": "sha512-OnNdfAr1FUQg7ksb7bmbKoby4qFOHw6DKWWUNB9KqnnCldxhxJlP+21dpyaWFmf2h0rTbOkXJtAGevY3XW1eew==", "requires": { - "@babel/compat-data": "^7.12.1", - "@babel/helper-compilation-targets": "^7.12.1", - "@babel/helper-module-imports": "^7.12.1", + "@babel/compat-data": "^7.12.7", + "@babel/helper-compilation-targets": "^7.12.5", + "@babel/helper-module-imports": "^7.12.5", "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-validator-option": "^7.12.1", "@babel/plugin-proposal-async-generator-functions": "^7.12.1", @@ -989,10 +979,10 @@ "@babel/plugin-proposal-json-strings": "^7.12.1", "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.7", "@babel/plugin-proposal-object-rest-spread": "^7.12.1", "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", "@babel/plugin-proposal-private-methods": "^7.12.1", "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0", @@ -1034,14 +1024,14 @@ "@babel/plugin-transform-reserved-words": "^7.12.1", "@babel/plugin-transform-shorthand-properties": "^7.12.1", "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.7", "@babel/plugin-transform-template-literals": "^7.12.1", "@babel/plugin-transform-typeof-symbol": "^7.12.1", "@babel/plugin-transform-unicode-escapes": "^7.12.1", "@babel/plugin-transform-unicode-regex": "^7.12.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.1", - "core-js-compat": "^3.6.2", + "@babel/types": "^7.12.7", + "core-js-compat": "^3.7.0", "semver": "^5.5.0" }, "dependencies": { @@ -1065,14 +1055,14 @@ } }, "@babel/preset-react": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.1.tgz", - "integrity": "sha512-euCExymHCi0qB9u5fKw7rvlw7AZSjw/NaB9h7EkdTt5+yHRrXdiRTh7fkG3uBPpJg82CqLfp1LHLqWGSCrab+g==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.7.tgz", + "integrity": "sha512-wKeTdnGUP5AEYCYQIMeXMMwU7j+2opxrG0WzuZfxuuW9nhKvvALBjl67653CWamZJVefuJGI219G591RSldrqQ==", "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-transform-react-display-name": "^7.12.1", - "@babel/plugin-transform-react-jsx": "^7.12.1", - "@babel/plugin-transform-react-jsx-development": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.7", + "@babel/plugin-transform-react-jsx-development": "^7.12.7", "@babel/plugin-transform-react-jsx-self": "^7.12.1", "@babel/plugin-transform-react-jsx-source": "^7.12.1", "@babel/plugin-transform-react-pure-annotations": "^7.12.1" @@ -1088,52 +1078,52 @@ } }, "@babel/runtime": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", - "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz", - "integrity": "sha512-umhPIcMrlBZ2aTWlWjUseW9LjQKxi1dpFlQS8DzsxB//5K+u6GLTC/JliPKHsd5kJVPIU6X/Hy0YvWOYPcMxBw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", "requires": { "core-js-pure": "^3.0.0", "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" } }, "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", + "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", + "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1564,9 +1554,9 @@ } }, "@types/yargs": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", - "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "version": "15.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", + "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -1645,23 +1635,6 @@ } } }, - "@testing-library/jest-dom": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz", - "integrity": "sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.5.1", - "chalk": "^2.4.1", - "css": "^2.2.3", - "css.escape": "^1.5.1", - "jest-diff": "^24.0.0", - "jest-matcher-utils": "^24.0.0", - "lodash": "^4.17.11", - "pretty-format": "^24.0.0", - "redent": "^3.0.0" - } - }, "@testing-library/react": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz", @@ -1680,9 +1653,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.1.11", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.11.tgz", - "integrity": "sha512-E5nSOzrjnvhURYnbOR2dClTqcyhPbPvtEwLHf7JJADKedPbcZsoJVfP+I2vBNfBjz4bnZIuhL/tNmRi5nJ7Jlw==", + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", "requires": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", @@ -1700,18 +1673,18 @@ } }, "@types/babel__template": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", - "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", "requires": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "@types/babel__traverse": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", - "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "version": "7.0.16", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.16.tgz", + "integrity": "sha512-S63Dt4CZOkuTmpLGGWtT/mQdVORJOpx6SZWGVaP56dda/0Nx5nEe82K7/LAm8zYr6SfMq+1N2OreIOrHAx656w==", "requires": { "@babel/types": "^7.3.0" } @@ -1731,9 +1704,9 @@ }, "dependencies": { "@types/node": { - "version": "14.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", - "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==" + "version": "14.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", + "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==" } } }, @@ -1775,15 +1748,6 @@ "@types/istanbul-lib-report": "*" } }, - "@types/jest": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.9.1.tgz", - "integrity": "sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q==", - "dev": true, - "requires": { - "jest-diff": "^24.3.0" - } - }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", @@ -1795,9 +1759,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "12.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.3.tgz", - "integrity": "sha512-8Jduo8wvvwDzEVJCOvS/G6sgilOLvvhn1eMmK3TW8/T217O7u1jdrK6ImKLv80tVryaPSVeKu6sjDEiFjd4/eg==", + "version": "12.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.7.tgz", + "integrity": "sha512-zvjOU1g4CpPilbTDUATnZCUb/6lARMRAqzT7ILwl1P3YvU2leEcZ2+fw9+Jrw/paXB1CgQyXTrN4hWDtqT9O2A==", "dev": true }, "@types/parse-json": { @@ -1817,9 +1781,9 @@ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" }, "@types/react": { - "version": "16.9.55", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.55.tgz", - "integrity": "sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.2.tgz", + "integrity": "sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ==", "dev": true, "requires": { "@types/prop-types": "*", @@ -1827,18 +1791,18 @@ } }, "@types/react-dom": { - "version": "16.9.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz", - "integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==", + "version": "16.9.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.10.tgz", + "integrity": "sha512-ItatOrnXDMAYpv6G8UCk2VhbYVTjZT9aorLtA/OzDN9XJ2GKcfam68jutoAcILdRjsRUO8qb7AmyObF77Q8QFw==", "dev": true, "requires": { - "@types/react": "*" + "@types/react": "^16" } }, "@types/react-native": { - "version": "0.63.30", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.30.tgz", - "integrity": "sha512-8/PrOjuUaPTCfMeW12ubseZPUGdbRhxYDa/aT+0D0KWVTe60b4H/gJrcfJmBXC6EcCFcimuTzQCv8/S03slYqA==", + "version": "0.63.37", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.37.tgz", + "integrity": "sha512-xr9SZG7tQQBKT6840tAGaWEC65D2gjyxZtuZxz631UgeW1ofItuu9HMVhoyYqot2hRSa6Q4YC8FYkRVUpM53/w==", "dev": true, "requires": { "@types/react": "*" @@ -1915,9 +1879,9 @@ } }, "@types/yargs": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", - "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "version": "15.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", + "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2448,33 +2412,15 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" }, "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", "is-string": "^1.0.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "array-union": { @@ -2496,32 +2442,13 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-abstract": "^1.18.0-next.1" } }, "arrify": { @@ -2636,9 +2563,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axobject-query": { "version": "2.2.0", @@ -2801,9 +2728,9 @@ } }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2838,9 +2765,9 @@ "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==" }, "babel-plugin-styled-components": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.11.1.tgz", - "integrity": "sha512-YwrInHyKUk1PU3avIRdiLyCpM++18Rs1NgyMXEAQC33rIXs/vro0A+stf4sT0Gf22Got+xRWB8Cm0tw+qkRzBA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz", + "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==", "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", "@babel/helper-module-imports": "^7.0.0", @@ -3052,9 +2979,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "regenerator-runtime": { "version": "0.11.1", @@ -3129,9 +3056,9 @@ } }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -3156,15 +3083,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3331,19 +3249,12 @@ } }, "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "requires": { - "bn.js": "^4.1.0", + "bn.js": "^5.0.0", "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" - } } }, "browserify-sign": { @@ -3378,14 +3289,15 @@ } }, "browserslist": { - "version": "4.14.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", - "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", "requires": { - "caniuse-lite": "^1.0.30001135", - "electron-to-chromium": "^1.3.571", - "escalade": "^3.1.0", - "node-releases": "^1.1.61" + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" } }, "bser": { @@ -3489,6 +3401,15 @@ "unset-value": "^1.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -3546,9 +3467,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001154", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001154.tgz", - "integrity": "sha512-y9DvdSti8NnYB9Be92ddMZQrcOe04kcQtcxtBx4NkB04+qZ+JUWotnXBJTmxlKudhxNTQ3RRknMwNU2YQl/Org==" + "version": "1.0.30001161", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001161.tgz", + "integrity": "sha512-JharrCDxOqPLBULF9/SPa6yMcBRTjZARJ6sc3cuKrPfyIk64JN6kuMINWqA99Xc8uElMFcROliwtz0n9pYej+g==" }, "capture-exit": { "version": "2.0.0", @@ -4021,16 +3942,16 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.0.tgz", + "integrity": "sha512-W2VYNB0nwQQE7tKS7HzXd7r2y/y2SVJl4ga6oH/dnaLFzM0o2lB2P3zCkWj5Wc/zyMYjtgd5Hmhk0ObkQFZOIA==" }, "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.0.tgz", + "integrity": "sha512-o9QKelQSxQMYWHXc/Gc4L8bx/4F7TTraE5rhuN8I7mKBt5dBIUpXpIR3omv70ebr8ST5R3PqbDQr+ZI3+Tt1FQ==", "requires": { - "browserslist": "^4.8.5", + "browserslist": "^4.14.7", "semver": "7.0.0" }, "dependencies": { @@ -4042,9 +3963,9 @@ } }, "core-js-pure": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", - "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.0.tgz", + "integrity": "sha512-fRjhg3NeouotRoIV0L1FdchA6CK7ZD+lyINyMoz19SyV+ROpC4noS1xItWHFtwZdlqfMfVPJEyEGdfri2bD1pA==" }, "core-util-is": { "version": "1.0.2", @@ -4292,12 +4213,6 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", - "dev": true - }, "cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -4380,26 +4295,26 @@ "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" }, "csso": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.1.0.tgz", - "integrity": "sha512-h+6w/W1WqXaJA4tb1dk7r5tVbOm97MsKxzwnvOR04UQ6GILroryjMWu3pmCCtL2mLaEStQ0fZgeGiy99mo7iyg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "requires": { - "css-tree": "^1.0.0" + "css-tree": "^1.1.2" }, "dependencies": { "css-tree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0.tgz", - "integrity": "sha512-CdVYz/Yuqw0VdKhXPBIgi8DO3NicJVYZNWeX9XcIuSp9ZoFT5IcleVRW07O5rMjdcx1mb+MEJPknTTEW7DdsYw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", + "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", "requires": { - "mdn-data": "2.0.12", + "mdn-data": "2.0.14", "source-map": "^0.6.1" } }, "mdn-data": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.12.tgz", - "integrity": "sha512-ULbAlgzVb8IqZ0Hsxm6hHSlQl3Jckst2YEQS7fODu9ilNWy2LvcoSY7TRFIktABP2mdppBioc66va90T+NUs8Q==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "source-map": { "version": "0.6.1", @@ -4422,9 +4337,9 @@ } }, "csstype": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", - "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", "dev": true }, "cyclist": { @@ -4477,9 +4392,9 @@ } }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { "ms": "2.1.2" } @@ -4887,9 +4802,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.585", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.585.tgz", - "integrity": "sha512-xoeqjMQhgHDZM7FiglJAb2aeOxHZWFruUc3MbAGTgE7GB8rr5fTn1Sdh5THGuQtndU3GuXlu91ZKqRivxoCZ/A==" + "version": "1.3.610", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.610.tgz", + "integrity": "sha512-eFDC+yVQpEhtlapk4CYDPfV9ajF9cEof5TBcO49L1ETO+aYogrKWDmYpZyxBScMNe8Bo/gJamH4amQ4yyvXg4g==" }, "elliptic": { "version": "6.5.3", @@ -5158,9 +5073,9 @@ } }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5488,11 +5403,11 @@ } }, "resolve": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", - "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "requires": { - "is-core-module": "^2.0.0", + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } @@ -5974,12 +5889,6 @@ "schema-utils": "^2.5.0" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "filesize": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", @@ -6311,6 +6220,16 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -6914,9 +6833,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inquirer": { "version": "7.3.3", @@ -7116,9 +7035,9 @@ } }, "is-core-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", - "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "requires": { "has": "^1.0.3" } @@ -7618,11 +7537,7 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "optional": true } } }, @@ -8265,9 +8180,9 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, "loglevel": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", - "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" }, "loose-envify": { "version": "1.4.0", @@ -8506,12 +8421,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, "mini-create-react-context": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", @@ -8696,12 +8605,6 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "optional": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -8877,9 +8780,9 @@ } }, "node-releases": { - "version": "1.1.65", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.65.tgz", - "integrity": "sha512-YpzJOe2WFIW0V4ZkJQd/DGR/zdVwc/pI4Nl1CZrBO19FdRcSTmsuhdttw9rsTzzJLrNcSloLiBbEYx1C4f6gpA==" + "version": "1.1.67", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", + "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==" }, "normalize-package-data": { "version": "2.5.0", @@ -8990,12 +8893,12 @@ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-is": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", - "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "object-keys": { @@ -9017,104 +8920,46 @@ } }, "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", - "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "object.fromentries": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", - "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", + "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", + "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "es-abstract": "^1.18.0-next.1" } }, "object.pick": { @@ -9126,34 +8971,14 @@ } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "obuf": { @@ -9564,9 +9389,9 @@ }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "requires": { "ms": "^2.1.1" } @@ -11138,20 +10963,10 @@ "minimatch": "3.0.4" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { "version": "8.2.0", @@ -12102,9 +11917,9 @@ }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "requires": { "ms": "^2.1.1" } @@ -12267,9 +12082,19 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.4.tgz", + "integrity": "sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } }, "static-extend": { "version": "0.1.2", @@ -12443,54 +12268,35 @@ } }, "string.prototype.matchall": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz", - "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", + "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", + "es-abstract": "^1.18.0-next.1", "has-symbols": "^1.0.1", "internal-slot": "^1.0.2", "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "side-channel": "^1.0.3" } }, "string.prototype.trimend": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", - "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", - "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -12559,15 +12365,6 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -13383,20 +13180,20 @@ } }, "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "requires": { "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "watchpack-chokidar2": "^2.0.1" } }, "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", "optional": true, "requires": { "chokidar": "^2.1.8" @@ -13432,11 +13229,7 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "optional": true }, "glob-parent": { "version": "3.1.0", @@ -13722,11 +13515,7 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } + "optional": true }, "glob-parent": { "version": "3.1.0", @@ -13908,9 +13697,9 @@ } }, "whatwg-fetch": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz", - "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz", + "integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==" }, "whatwg-mimetype": { "version": "2.3.0", diff --git a/examples/react-typescript/package.json b/examples/react-typescript/package.json index 82af2831..cb62b976 100644 --- a/examples/react-typescript/package.json +++ b/examples/react-typescript/package.json @@ -15,10 +15,8 @@ "typescript": "^3.9.7" }, "devDependencies": { - "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", - "@types/jest": "^24.0.0", "@types/node": "^12.0.0", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", diff --git a/examples/react-typescript/src/App.tsx b/examples/react-typescript/src/App.tsx index d4bf5e07..2eac5614 100644 --- a/examples/react-typescript/src/App.tsx +++ b/examples/react-typescript/src/App.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import "./App.css"; import { useAgile, useEvent, useWatcher } from "@agile-ts/react"; import { - multiEditor, MY_COLLECTION, MY_COMPUTED, MY_EVENT, @@ -49,8 +48,6 @@ const App = (props: any) => { console.log("MY_STATE changes"); }); - useAgile(multiEditor.deps); - // Create global Instance of Core (for better debugging) useEffect(() => { globalBind("__core__", { ...require("./core") }); @@ -120,43 +117,8 @@ const App = (props: any) => { - - -
- - { - multiEditor.setValue("name", e.target.value, { background: false }); - }} - value={multiEditor.getValueById("name")} - /> - {multiEditor.getStatus("name")?.type === "error" && ( -

- {multiEditor.getStatus("name")?.message} -

- )} - - - multiEditor.setValue("email", e.target.value)} - /> - {multiEditor.getStatus("email")?.type === "error" && ( -

- {multiEditor.getStatus("email")?.message} -

- )} -

{rerenderCount}

-
- + ); }; diff --git a/examples/react-typescript/src/core/index.ts b/examples/react-typescript/src/core/index.ts index 5a7d8f45..cb2582c5 100644 --- a/examples/react-typescript/src/core/index.ts +++ b/examples/react-typescript/src/core/index.ts @@ -1,12 +1,13 @@ -import { Agile } from "@agile-ts/core"; -import { MultiEditor, Validator } from "@agile-ts/multieditor"; +import { Agile, clone, Logger } from "@agile-ts/core"; export const App = new Agile({ - logConfig: { active: true }, + logConfig: { level: Logger.level.DEBUG }, }); -export const MY_STATE = App.State("MyState", "my-state"); //.persist(); -export const MY_STATE_2 = App.State("MyState2").persist("my-state2"); +export const MY_STATE = App.State("MyState", { key: "my-state" }); //.persist(); +export const MY_STATE_2 = App.State("MyState2", { + key: "my-state2", +}).persist(); MY_STATE_2.onLoad(() => { console.log("On Load"); }); @@ -18,7 +19,7 @@ MY_STATE.watch("test", (value: any) => { export const MY_COMPUTED = App.Computed(() => { return "test" + MY_STATE.value + "_computed_" + MY_STATE_2.value; -}); +}, []); interface collectionValueInterface { id: string; @@ -39,12 +40,15 @@ export const MY_COLLECTION = App.Collection( MY_COLLECTION.collect({ id: "id1", name: "test" }); MY_COLLECTION.collect({ id: "id2", name: "test2" }, "myGroup"); MY_COLLECTION.update("id1", { id: "id1Updated", name: "testUpdated" }); -MY_COLLECTION.getGroup("myGroup")?.persist({ followCollectionPattern: true }); +MY_COLLECTION.getGroup("myGroup")?.persist({ + followCollectionPersistKeyPattern: true, +}); -console.log("Initial: myCollection ", MY_COLLECTION); +console.log("Initial: myCollection ", clone(MY_COLLECTION)); export const MY_EVENT = App.Event<{ name: string }>({ delay: 3000, + key: "myEvent", }); MY_EVENT.on(() => { @@ -55,51 +59,6 @@ MY_EVENT.on("Test", () => { console.log("Triggered Event (Test)"); }); -// MULTIEDITOR TEST - -const emailValidator = new Validator().string().email().required(); - -export const multiEditor = new MultiEditor( - (editor) => ({ - data: { - id: "myId", - email: undefined, - name: undefined, - }, - onSubmit: async (data) => { - console.log("Submitted MultiEditor", data); - return Promise.resolve(true); - }, - fixedProperties: ["id"], - validateMethods: { - email: emailValidator, - name: editor - .Validator() - .required() - .string() - .max(10) - .min(2) - .addValidationMethod("testFuck", (key, value, editor) => { - const isValid = value !== "Fuck"; - - if (!isValid) { - editor.setStatus(key, "error", "Fuck is no valid Name!"); - } - - return Promise.resolve(isValid); - }), - }, - computeMethods: { - name: (value) => { - return value ? value?.charAt(0).toUpperCase() + value?.slice(1) : value; - }, - }, - editableProperties: ["email", "name"], - reValidateMode: "onChange", - validate: "all", - }) -); - // LOGGER tests /* diff --git a/jest.config.base.js b/jest.config.base.js new file mode 100644 index 00000000..5e360e67 --- /dev/null +++ b/jest.config.base.js @@ -0,0 +1,9 @@ +module.exports = { + testEnvironment: "node", + coveragePathIgnorePatterns: ["(tests/.*.mock).(jsx?|tsx?)$"], + modulePathIgnorePatterns: ["dist"], + testMatch: ["/packages/**/tests/**/*.test.ts"], + transform: { + "^.+\\.ts?$": "ts-jest", + }, +}; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..a77ee7ec --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +const baseConfig = require("./jest.config.base.js"); + +module.exports = { + ...baseConfig, + projects: ["/packages/*/jest.config.js"], +}; diff --git a/package-lock.json b/package-lock.json index 42103725..2b444e92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,175 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", @@ -29,6 +192,186 @@ "js-tokens": "^4.0.0" } }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, "@evocateur/libnpmaccess": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz", @@ -141,4614 +484,7812 @@ } } }, - "@lerna/add": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.21.0.tgz", - "integrity": "sha512-vhUXXF6SpufBE1EkNEXwz1VLW03f177G9uMOFMQkp6OJ30/PWg4Ekifuz9/3YfgB2/GH8Tu4Lk3O51P2Hskg/A==", - "dev": true, - "requires": { - "@evocateur/pacote": "^9.6.3", - "@lerna/bootstrap": "3.21.0", - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/npm-conf": "3.16.0", - "@lerna/validation-error": "3.13.0", - "dedent": "^0.7.0", - "npm-package-arg": "^6.1.0", - "p-map": "^2.1.0", - "semver": "^6.2.0" - } - }, - "@lerna/bootstrap": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.21.0.tgz", - "integrity": "sha512-mtNHlXpmvJn6JTu0KcuTTPl2jLsDNud0QacV/h++qsaKbhAaJr/FElNZ5s7MwZFUM3XaDmvWzHKaszeBMHIbBw==", + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/has-npm-version": "3.16.5", - "@lerna/npm-install": "3.16.5", - "@lerna/package-graph": "3.18.5", - "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.16.5", - "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-topologically": "3.18.5", - "@lerna/symlink-binary": "3.17.0", - "@lerna/symlink-dependencies": "3.17.0", - "@lerna/validation-error": "3.13.0", - "dedent": "^0.7.0", - "get-port": "^4.2.0", - "multimatch": "^3.0.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "p-finally": "^1.0.0", - "p-map": "^2.1.0", - "p-map-series": "^1.0.0", - "p-waterfall": "^1.0.0", - "read-package-tree": "^5.1.6", - "semver": "^6.2.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } } }, - "@lerna/changed": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.21.0.tgz", - "integrity": "sha512-hzqoyf8MSHVjZp0gfJ7G8jaz+++mgXYiNs9iViQGA8JlN/dnWLI5sWDptEH3/B30Izo+fdVz0S0s7ydVE3pWIw==", - "dev": true, - "requires": { - "@lerna/collect-updates": "3.20.0", - "@lerna/command": "3.21.0", - "@lerna/listable": "3.18.5", - "@lerna/output": "3.13.0" - } + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true }, - "@lerna/check-working-tree": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.16.5.tgz", - "integrity": "sha512-xWjVBcuhvB8+UmCSb5tKVLB5OuzSpw96WEhS2uz6hkWVa/Euh1A0/HJwn2cemyK47wUrCQXtczBUiqnq9yX5VQ==", + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", "dev": true, "requires": { - "@lerna/collect-uncommitted": "3.16.5", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@lerna/add": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.21.0.tgz", + "integrity": "sha512-vhUXXF6SpufBE1EkNEXwz1VLW03f177G9uMOFMQkp6OJ30/PWg4Ekifuz9/3YfgB2/GH8Tu4Lk3O51P2Hskg/A==", + "dev": true, + "requires": { + "@evocateur/pacote": "^9.6.3", + "@lerna/bootstrap": "3.21.0", + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "npm-package-arg": "^6.1.0", + "p-map": "^2.1.0", + "semver": "^6.2.0" + } + }, + "@lerna/bootstrap": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.21.0.tgz", + "integrity": "sha512-mtNHlXpmvJn6JTu0KcuTTPl2jLsDNud0QacV/h++qsaKbhAaJr/FElNZ5s7MwZFUM3XaDmvWzHKaszeBMHIbBw==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/has-npm-version": "3.16.5", + "@lerna/npm-install": "3.16.5", + "@lerna/package-graph": "3.18.5", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/rimraf-dir": "3.16.5", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.5", + "@lerna/symlink-binary": "3.17.0", + "@lerna/symlink-dependencies": "3.17.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "get-port": "^4.2.0", + "multimatch": "^3.0.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0", + "p-waterfall": "^1.0.0", + "read-package-tree": "^5.1.6", + "semver": "^6.2.0" + } + }, + "@lerna/changed": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.21.0.tgz", + "integrity": "sha512-hzqoyf8MSHVjZp0gfJ7G8jaz+++mgXYiNs9iViQGA8JlN/dnWLI5sWDptEH3/B30Izo+fdVz0S0s7ydVE3pWIw==", + "dev": true, + "requires": { + "@lerna/collect-updates": "3.20.0", + "@lerna/command": "3.21.0", + "@lerna/listable": "3.18.5", + "@lerna/output": "3.13.0" + } + }, + "@lerna/check-working-tree": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.16.5.tgz", + "integrity": "sha512-xWjVBcuhvB8+UmCSb5tKVLB5OuzSpw96WEhS2uz6hkWVa/Euh1A0/HJwn2cemyK47wUrCQXtczBUiqnq9yX5VQ==", + "dev": true, + "requires": { + "@lerna/collect-uncommitted": "3.16.5", + "@lerna/describe-ref": "3.16.5", + "@lerna/validation-error": "3.13.0" + } + }, + "@lerna/child-process": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.16.5.tgz", + "integrity": "sha512-vdcI7mzei9ERRV4oO8Y1LHBZ3A5+ampRKg1wq5nutLsUA4mEBN6H7JqjWOMY9xZemv6+kATm2ofjJ3lW5TszQg==", + "dev": true, + "requires": { + "chalk": "^2.3.1", + "execa": "^1.0.0", + "strong-log-transformer": "^2.0.0" + } + }, + "@lerna/clean": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.21.0.tgz", + "integrity": "sha512-b/L9l+MDgE/7oGbrav6rG8RTQvRiZLO1zTcG17zgJAAuhlsPxJExMlh2DFwJEVi2les70vMhHfST3Ue1IMMjpg==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/prompt": "3.18.5", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/rimraf-dir": "3.16.5", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0", + "p-waterfall": "^1.0.0" + } + }, + "@lerna/cli": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.18.5.tgz", + "integrity": "sha512-erkbxkj9jfc89vVs/jBLY/fM0I80oLmJkFUV3Q3wk9J3miYhP14zgVEBsPZY68IZlEjT6T3Xlq2xO1AVaatHsA==", + "dev": true, + "requires": { + "@lerna/global-options": "3.13.0", + "dedent": "^0.7.0", + "npmlog": "^4.1.2", + "yargs": "^14.2.2" + } + }, + "@lerna/collect-uncommitted": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.16.5.tgz", + "integrity": "sha512-ZgqnGwpDZiWyzIQVZtQaj9tRizsL4dUOhuOStWgTAw1EMe47cvAY2kL709DzxFhjr6JpJSjXV5rZEAeU3VE0Hg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "chalk": "^2.3.1", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/collect-updates": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.20.0.tgz", + "integrity": "sha512-qBTVT5g4fupVhBFuY4nI/3FSJtQVcDh7/gEPOpRxoXB/yCSnT38MFHXWl+y4einLciCjt/+0x6/4AG80fjay2Q==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/describe-ref": "3.16.5", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "slash": "^2.0.0" + } + }, + "@lerna/command": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.21.0.tgz", + "integrity": "sha512-T2bu6R8R3KkH5YoCKdutKv123iUgUbW8efVjdGCDnCMthAQzoentOJfDeodBwn0P2OqCl3ohsiNVtSn9h78fyQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/package-graph": "3.18.5", + "@lerna/project": "3.21.0", + "@lerna/validation-error": "3.13.0", + "@lerna/write-log-file": "3.13.0", + "clone-deep": "^4.0.1", + "dedent": "^0.7.0", + "execa": "^1.0.0", + "is-ci": "^2.0.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/conventional-commits": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.22.0.tgz", + "integrity": "sha512-z4ZZk1e8Mhz7+IS8NxHr64wyklHctCJyWpJKEZZPJiLFJ8yKto/x38O80R10pIzC0rr8Sy/OsjSH4bl0TbbgqA==", + "dev": true, + "requires": { + "@lerna/validation-error": "3.13.0", + "conventional-changelog-angular": "^5.0.3", + "conventional-changelog-core": "^3.1.6", + "conventional-recommended-bump": "^5.0.0", + "fs-extra": "^8.1.0", + "get-stream": "^4.0.0", + "lodash.template": "^4.5.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "pify": "^4.0.1", + "semver": "^6.2.0" + } + }, + "@lerna/create": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.22.0.tgz", + "integrity": "sha512-MdiQQzCcB4E9fBF1TyMOaAEz9lUjIHp1Ju9H7f3lXze5JK6Fl5NYkouAvsLgY6YSIhXMY8AHW2zzXeBDY4yWkw==", + "dev": true, + "requires": { + "@evocateur/pacote": "^9.6.3", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.21.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/validation-error": "3.13.0", + "camelcase": "^5.0.0", + "dedent": "^0.7.0", + "fs-extra": "^8.1.0", + "globby": "^9.2.0", + "init-package-json": "^1.10.3", + "npm-package-arg": "^6.1.0", + "p-reduce": "^1.0.0", + "pify": "^4.0.1", + "semver": "^6.2.0", + "slash": "^2.0.0", + "validate-npm-package-license": "^3.0.3", + "validate-npm-package-name": "^3.0.0", + "whatwg-url": "^7.0.0" + } + }, + "@lerna/create-symlink": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.16.2.tgz", + "integrity": "sha512-pzXIJp6av15P325sgiIRpsPXLFmkisLhMBCy4764d+7yjf2bzrJ4gkWVMhsv4AdF0NN3OyZ5jjzzTtLNqfR+Jw==", + "dev": true, + "requires": { + "@zkochan/cmd-shim": "^3.1.0", + "fs-extra": "^8.1.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/describe-ref": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.16.5.tgz", + "integrity": "sha512-c01+4gUF0saOOtDBzbLMFOTJDHTKbDFNErEY6q6i9QaXuzy9LNN62z+Hw4acAAZuJQhrVWncVathcmkkjvSVGw==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "npmlog": "^4.1.2" + } + }, + "@lerna/diff": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.21.0.tgz", + "integrity": "sha512-5viTR33QV3S7O+bjruo1SaR40m7F2aUHJaDAC7fL9Ca6xji+aw1KFkpCtVlISS0G8vikUREGMJh+c/VMSc8Usw==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.21.0", + "@lerna/validation-error": "3.13.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/exec": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.21.0.tgz", + "integrity": "sha512-iLvDBrIE6rpdd4GIKTY9mkXyhwsJ2RvQdB9ZU+/NhR3okXfqKc6py/24tV111jqpXTtZUW6HNydT4dMao2hi1Q==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/profiler": "3.20.0", + "@lerna/run-topologically": "3.18.5", + "@lerna/validation-error": "3.13.0", + "p-map": "^2.1.0" + } + }, + "@lerna/filter-options": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.20.0.tgz", + "integrity": "sha512-bmcHtvxn7SIl/R9gpiNMVG7yjx7WyT0HSGw34YVZ9B+3xF/83N3r5Rgtjh4hheLZ+Q91Or0Jyu5O3Nr+AwZe2g==", + "dev": true, + "requires": { + "@lerna/collect-updates": "3.20.0", + "@lerna/filter-packages": "3.18.0", + "dedent": "^0.7.0", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/filter-packages": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.18.0.tgz", + "integrity": "sha512-6/0pMM04bCHNATIOkouuYmPg6KH3VkPCIgTfQmdkPJTullERyEQfNUKikrefjxo1vHOoCACDpy65JYyKiAbdwQ==", + "dev": true, + "requires": { + "@lerna/validation-error": "3.13.0", + "multimatch": "^3.0.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/get-npm-exec-opts": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.13.0.tgz", + "integrity": "sha512-Y0xWL0rg3boVyJk6An/vurKzubyJKtrxYv2sj4bB8Mc5zZ3tqtv0ccbOkmkXKqbzvNNF7VeUt1OJ3DRgtC/QZw==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/get-packed": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.16.0.tgz", + "integrity": "sha512-AjsFiaJzo1GCPnJUJZiTW6J1EihrPkc2y3nMu6m3uWFxoleklsSCyImumzVZJssxMi3CPpztj8LmADLedl9kXw==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "ssri": "^6.0.1", + "tar": "^4.4.8" + } + }, + "@lerna/github-client": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.22.0.tgz", + "integrity": "sha512-O/GwPW+Gzr3Eb5bk+nTzTJ3uv+jh5jGho9BOqKlajXaOkMYGBELEAqV5+uARNGWZFvYAiF4PgqHb6aCUu7XdXg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@octokit/plugin-enterprise-rest": "^6.0.1", + "@octokit/rest": "^16.28.4", + "git-url-parse": "^11.1.2", + "npmlog": "^4.1.2" + } + }, + "@lerna/gitlab-client": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-3.15.0.tgz", + "integrity": "sha512-OsBvRSejHXUBMgwWQqNoioB8sgzL/Pf1pOUhHKtkiMl6aAWjklaaq5HPMvTIsZPfS6DJ9L5OK2GGZuooP/5c8Q==", + "dev": true, + "requires": { + "node-fetch": "^2.5.0", + "npmlog": "^4.1.2", + "whatwg-url": "^7.0.0" + } + }, + "@lerna/global-options": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.13.0.tgz", + "integrity": "sha512-SlZvh1gVRRzYLVluz9fryY1nJpZ0FHDGB66U9tFfvnnxmueckRQxLopn3tXj3NU1kc3QANT2I5BsQkOqZ4TEFQ==", + "dev": true + }, + "@lerna/has-npm-version": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.5.tgz", + "integrity": "sha512-WL7LycR9bkftyqbYop5rEGJ9sRFIV55tSGmbN1HLrF9idwOCD7CLrT64t235t3t4O5gehDnwKI5h2U3oxTrF8Q==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "semver": "^6.2.0" + } + }, + "@lerna/import": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.22.0.tgz", + "integrity": "sha512-uWOlexasM5XR6tXi4YehODtH9Y3OZrFht3mGUFFT3OIl2s+V85xIGFfqFGMTipMPAGb2oF1UBLL48kR43hRsOg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.21.0", + "@lerna/prompt": "3.18.5", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "fs-extra": "^8.1.0", + "p-map-series": "^1.0.0" + } + }, + "@lerna/info": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/info/-/info-3.21.0.tgz", + "integrity": "sha512-0XDqGYVBgWxUquFaIptW2bYSIu6jOs1BtkvRTWDDhw4zyEdp6q4eaMvqdSap1CG+7wM5jeLCi6z94wS0AuiuwA==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/output": "3.13.0", + "envinfo": "^7.3.1" + } + }, + "@lerna/init": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.21.0.tgz", + "integrity": "sha512-6CM0z+EFUkFfurwdJCR+LQQF6MqHbYDCBPyhu/d086LRf58GtYZYj49J8mKG9ktayp/TOIxL/pKKjgLD8QBPOg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.21.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0", + "write-json-file": "^3.2.0" + } + }, + "@lerna/link": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.21.0.tgz", + "integrity": "sha512-tGu9GxrX7Ivs+Wl3w1+jrLi1nQ36kNI32dcOssij6bg0oZ2M2MDEFI9UF2gmoypTaN9uO5TSsjCFS7aR79HbdQ==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/package-graph": "3.18.5", + "@lerna/symlink-dependencies": "3.17.0", + "p-map": "^2.1.0", + "slash": "^2.0.0" + } + }, + "@lerna/list": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.21.0.tgz", + "integrity": "sha512-KehRjE83B1VaAbRRkRy6jLX1Cin8ltsrQ7FHf2bhwhRHK0S54YuA6LOoBnY/NtA8bHDX/Z+G5sMY78X30NS9tg==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/listable": "3.18.5", + "@lerna/output": "3.13.0" + } + }, + "@lerna/listable": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.18.5.tgz", + "integrity": "sha512-Sdr3pVyaEv5A7ZkGGYR7zN+tTl2iDcinryBPvtuv20VJrXBE8wYcOks1edBTcOWsPjCE/rMP4bo1pseyk3UTsg==", + "dev": true, + "requires": { + "@lerna/query-graph": "3.18.5", + "chalk": "^2.3.1", + "columnify": "^1.5.4" + } + }, + "@lerna/log-packed": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.16.0.tgz", + "integrity": "sha512-Fp+McSNBV/P2mnLUYTaSlG8GSmpXM7krKWcllqElGxvAqv6chk2K3c2k80MeVB4WvJ9tRjUUf+i7HUTiQ9/ckQ==", + "dev": true, + "requires": { + "byte-size": "^5.0.1", + "columnify": "^1.5.4", + "has-unicode": "^2.0.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/npm-conf": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.16.0.tgz", + "integrity": "sha512-HbO3DUrTkCAn2iQ9+FF/eisDpWY5POQAOF1m7q//CZjdC2HSW3UYbKEGsSisFxSfaF9Z4jtrV+F/wX6qWs3CuA==", + "dev": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^4.0.1" + } + }, + "@lerna/npm-dist-tag": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.5.tgz", + "integrity": "sha512-xw0HDoIG6HreVsJND9/dGls1c+lf6vhu7yJoo56Sz5bvncTloYGLUppIfDHQr4ZvmPCK8rsh0euCVh2giPxzKQ==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "@lerna/otplease": "3.18.5", + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/npm-install": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.5.tgz", + "integrity": "sha512-hfiKk8Eku6rB9uApqsalHHTHY+mOrrHeWEs+gtg7+meQZMTS3kzv4oVp5cBZigndQr3knTLjwthT/FX4KvseFg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/get-npm-exec-opts": "3.13.0", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "signal-exit": "^3.0.2", + "write-pkg": "^3.1.0" + } + }, + "@lerna/npm-publish": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.18.5.tgz", + "integrity": "sha512-3etLT9+2L8JAx5F8uf7qp6iAtOLSMj+ZYWY6oUgozPi/uLqU0/gsMsEXh3F0+YVW33q0M61RpduBoAlOOZnaTg==", + "dev": true, + "requires": { + "@evocateur/libnpmpublish": "^1.2.2", + "@lerna/otplease": "3.18.5", + "@lerna/run-lifecycle": "3.16.2", + "figgy-pudding": "^3.5.1", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "pify": "^4.0.1", + "read-package-json": "^2.0.13" + } + }, + "@lerna/npm-run-script": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.16.5.tgz", + "integrity": "sha512-1asRi+LjmVn3pMjEdpqKJZFT/3ZNpb+VVeJMwrJaV/3DivdNg7XlPK9LTrORuKU4PSvhdEZvJmSlxCKyDpiXsQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/get-npm-exec-opts": "3.13.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/otplease": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/otplease/-/otplease-3.18.5.tgz", + "integrity": "sha512-S+SldXAbcXTEDhzdxYLU0ZBKuYyURP/ND2/dK6IpKgLxQYh/z4ScljPDMyKymmEvgiEJmBsPZAAPfmNPEzxjog==", + "dev": true, + "requires": { + "@lerna/prompt": "3.18.5", + "figgy-pudding": "^3.5.1" + } + }, + "@lerna/output": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.13.0.tgz", + "integrity": "sha512-7ZnQ9nvUDu/WD+bNsypmPG5MwZBwu86iRoiW6C1WBuXXDxM5cnIAC1m2WxHeFnjyMrYlRXM9PzOQ9VDD+C15Rg==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/pack-directory": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.16.4.tgz", + "integrity": "sha512-uxSF0HZeGyKaaVHz5FroDY9A5NDDiCibrbYR6+khmrhZtY0Bgn6hWq8Gswl9iIlymA+VzCbshWIMX4o2O8C8ng==", + "dev": true, + "requires": { + "@lerna/get-packed": "3.16.0", + "@lerna/package": "3.16.0", + "@lerna/run-lifecycle": "3.16.2", + "figgy-pudding": "^3.5.1", + "npm-packlist": "^1.4.4", + "npmlog": "^4.1.2", + "tar": "^4.4.10", + "temp-write": "^3.4.0" + } + }, + "@lerna/package": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.16.0.tgz", + "integrity": "sha512-2lHBWpaxcBoiNVbtyLtPUuTYEaB/Z+eEqRS9duxpZs6D+mTTZMNy6/5vpEVSCBmzvdYpyqhqaYjjSLvjjr5Riw==", + "dev": true, + "requires": { + "load-json-file": "^5.3.0", + "npm-package-arg": "^6.1.0", + "write-pkg": "^3.1.0" + } + }, + "@lerna/package-graph": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.18.5.tgz", + "integrity": "sha512-8QDrR9T+dBegjeLr+n9WZTVxUYUhIUjUgZ0gvNxUBN8S1WB9r6H5Yk56/MVaB64tA3oGAN9IIxX6w0WvTfFudA==", + "dev": true, + "requires": { + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/validation-error": "3.13.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "semver": "^6.2.0" + } + }, + "@lerna/prerelease-id-from-version": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-3.16.0.tgz", + "integrity": "sha512-qZyeUyrE59uOK8rKdGn7jQz+9uOpAaF/3hbslJVFL1NqF9ELDTqjCPXivuejMX/lN4OgD6BugTO4cR7UTq/sZA==", + "dev": true, + "requires": { + "semver": "^6.2.0" + } + }, + "@lerna/profiler": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@lerna/profiler/-/profiler-3.20.0.tgz", + "integrity": "sha512-bh8hKxAlm6yu8WEOvbLENm42i2v9SsR4WbrCWSbsmOElx3foRnMlYk7NkGECa+U5c3K4C6GeBbwgqs54PP7Ljg==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "fs-extra": "^8.1.0", + "npmlog": "^4.1.2", + "upath": "^1.2.0" + } + }, + "@lerna/project": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.21.0.tgz", + "integrity": "sha512-xT1mrpET2BF11CY32uypV2GPtPVm6Hgtha7D81GQP9iAitk9EccrdNjYGt5UBYASl4CIDXBRxwmTTVGfrCx82A==", + "dev": true, + "requires": { + "@lerna/package": "3.16.0", + "@lerna/validation-error": "3.13.0", + "cosmiconfig": "^5.1.0", + "dedent": "^0.7.0", + "dot-prop": "^4.2.0", + "glob-parent": "^5.0.0", + "globby": "^9.2.0", + "load-json-file": "^5.3.0", + "npmlog": "^4.1.2", + "p-map": "^2.1.0", + "resolve-from": "^4.0.0", + "write-json-file": "^3.2.0" + } + }, + "@lerna/prompt": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/prompt/-/prompt-3.18.5.tgz", + "integrity": "sha512-rkKj4nm1twSbBEb69+Em/2jAERK8htUuV8/xSjN0NPC+6UjzAwY52/x9n5cfmpa9lyKf/uItp7chCI7eDmNTKQ==", + "dev": true, + "requires": { + "inquirer": "^6.2.0", + "npmlog": "^4.1.2" + } + }, + "@lerna/publish": { + "version": "3.22.1", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.22.1.tgz", + "integrity": "sha512-PG9CM9HUYDreb1FbJwFg90TCBQooGjj+n/pb3gw/eH5mEDq0p8wKdLFe0qkiqUkm/Ub5C8DbVFertIo0Vd0zcw==", + "dev": true, + "requires": { + "@evocateur/libnpmaccess": "^3.1.2", + "@evocateur/npm-registry-fetch": "^4.0.0", + "@evocateur/pacote": "^9.6.3", + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.20.0", + "@lerna/command": "3.21.0", "@lerna/describe-ref": "3.16.5", - "@lerna/validation-error": "3.13.0" + "@lerna/log-packed": "3.16.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/npm-dist-tag": "3.18.5", + "@lerna/npm-publish": "3.18.5", + "@lerna/otplease": "3.18.5", + "@lerna/output": "3.13.0", + "@lerna/pack-directory": "3.16.4", + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/prompt": "3.18.5", + "@lerna/pulse-till-done": "3.13.0", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.5", + "@lerna/validation-error": "3.13.0", + "@lerna/version": "3.22.1", + "figgy-pudding": "^3.5.1", + "fs-extra": "^8.1.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-pipe": "^1.2.0", + "semver": "^6.2.0" + } + }, + "@lerna/pulse-till-done": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-3.13.0.tgz", + "integrity": "sha512-1SOHpy7ZNTPulzIbargrgaJX387csN7cF1cLOGZiJQA6VqnS5eWs2CIrG8i8wmaUavj2QlQ5oEbRMVVXSsGrzA==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/query-graph": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.18.5.tgz", + "integrity": "sha512-50Lf4uuMpMWvJ306be3oQDHrWV42nai9gbIVByPBYJuVW8dT8O8pA3EzitNYBUdLL9/qEVbrR0ry1HD7EXwtRA==", + "dev": true, + "requires": { + "@lerna/package-graph": "3.18.5", + "figgy-pudding": "^3.5.1" + } + }, + "@lerna/resolve-symlink": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.16.0.tgz", + "integrity": "sha512-Ibj5e7njVHNJ/NOqT4HlEgPFPtPLWsO7iu59AM5bJDcAJcR96mLZ7KGVIsS2tvaO7akMEJvt2P+ErwCdloG3jQ==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "npmlog": "^4.1.2", + "read-cmd-shim": "^1.0.1" + } + }, + "@lerna/rimraf-dir": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.16.5.tgz", + "integrity": "sha512-bQlKmO0pXUsXoF8lOLknhyQjOZsCc0bosQDoX4lujBXSWxHVTg1VxURtWf2lUjz/ACsJVDfvHZbDm8kyBk5okA==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "npmlog": "^4.1.2", + "path-exists": "^3.0.0", + "rimraf": "^2.6.2" + } + }, + "@lerna/run": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.21.0.tgz", + "integrity": "sha512-fJF68rT3veh+hkToFsBmUJ9MHc9yGXA7LSDvhziAojzOb0AI/jBDp6cEcDQyJ7dbnplba2Lj02IH61QUf9oW0Q==", + "dev": true, + "requires": { + "@lerna/command": "3.21.0", + "@lerna/filter-options": "3.20.0", + "@lerna/npm-run-script": "3.16.5", + "@lerna/output": "3.13.0", + "@lerna/profiler": "3.20.0", + "@lerna/run-topologically": "3.18.5", + "@lerna/timer": "3.13.0", + "@lerna/validation-error": "3.13.0", + "p-map": "^2.1.0" + } + }, + "@lerna/run-lifecycle": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.16.2.tgz", + "integrity": "sha512-RqFoznE8rDpyyF0rOJy3+KjZCeTkO8y/OB9orPauR7G2xQ7PTdCpgo7EO6ZNdz3Al+k1BydClZz/j78gNCmL2A==", + "dev": true, + "requires": { + "@lerna/npm-conf": "3.16.0", + "figgy-pudding": "^3.5.1", + "npm-lifecycle": "^3.1.2", + "npmlog": "^4.1.2" + } + }, + "@lerna/run-topologically": { + "version": "3.18.5", + "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.18.5.tgz", + "integrity": "sha512-6N1I+6wf4hLOnPW+XDZqwufyIQ6gqoPfHZFkfWlvTQ+Ue7CuF8qIVQ1Eddw5HKQMkxqN10thKOFfq/9NQZ4NUg==", + "dev": true, + "requires": { + "@lerna/query-graph": "3.18.5", + "figgy-pudding": "^3.5.1", + "p-queue": "^4.0.0" + } + }, + "@lerna/symlink-binary": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.17.0.tgz", + "integrity": "sha512-RLpy9UY6+3nT5J+5jkM5MZyMmjNHxZIZvXLV+Q3MXrf7Eaa1hNqyynyj4RO95fxbS+EZc4XVSk25DGFQbcRNSQ==", + "dev": true, + "requires": { + "@lerna/create-symlink": "3.16.2", + "@lerna/package": "3.16.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0" + } + }, + "@lerna/symlink-dependencies": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.17.0.tgz", + "integrity": "sha512-KmjU5YT1bpt6coOmdFueTJ7DFJL4H1w5eF8yAQ2zsGNTtZ+i5SGFBWpb9AQaw168dydc3s4eu0W0Sirda+F59Q==", + "dev": true, + "requires": { + "@lerna/create-symlink": "3.16.2", + "@lerna/resolve-symlink": "3.16.0", + "@lerna/symlink-binary": "3.17.0", + "fs-extra": "^8.1.0", + "p-finally": "^1.0.0", + "p-map": "^2.1.0", + "p-map-series": "^1.0.0" + } + }, + "@lerna/timer": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/timer/-/timer-3.13.0.tgz", + "integrity": "sha512-RHWrDl8U4XNPqY5MQHkToWS9jHPnkLZEt5VD+uunCKTfzlxGnRCr3/zVr8VGy/uENMYpVP3wJa4RKGY6M0vkRw==", + "dev": true + }, + "@lerna/validation-error": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-3.13.0.tgz", + "integrity": "sha512-SiJP75nwB8GhgwLKQfdkSnDufAaCbkZWJqEDlKOUPUvVOplRGnfL+BPQZH5nvq2BYSRXsksXWZ4UHVnQZI/HYA==", + "dev": true, + "requires": { + "npmlog": "^4.1.2" + } + }, + "@lerna/version": { + "version": "3.22.1", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.22.1.tgz", + "integrity": "sha512-PSGt/K1hVqreAFoi3zjD0VEDupQ2WZVlVIwesrE5GbrL2BjXowjCsTDPqblahDUPy0hp6h7E2kG855yLTp62+g==", + "dev": true, + "requires": { + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.20.0", + "@lerna/command": "3.21.0", + "@lerna/conventional-commits": "3.22.0", + "@lerna/github-client": "3.22.0", + "@lerna/gitlab-client": "3.15.0", + "@lerna/output": "3.13.0", + "@lerna/prerelease-id-from-version": "3.16.0", + "@lerna/prompt": "3.18.5", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.5", + "@lerna/validation-error": "3.13.0", + "chalk": "^2.3.1", + "dedent": "^0.7.0", + "load-json-file": "^5.3.0", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "p-map": "^2.1.0", + "p-pipe": "^1.2.0", + "p-reduce": "^1.0.0", + "p-waterfall": "^1.0.0", + "semver": "^6.2.0", + "slash": "^2.0.0", + "temp-write": "^3.4.0", + "write-json-file": "^3.2.0" + } + }, + "@lerna/write-log-file": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-3.13.0.tgz", + "integrity": "sha512-RibeMnDPvlL8bFYW5C8cs4mbI3AHfQef73tnJCQ/SgrXZHehmHnsyWUiE7qDQCAo+B1RfTapvSyFF69iPj326A==", + "dev": true, + "requires": { + "npmlog": "^4.1.2", + "write-file-atomic": "^2.3.0" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@octokit/auth-token": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.3.tgz", + "integrity": "sha512-fdGoOQ3kQJh+hrilc0Plg50xSfaCKOeYN9t6dpJKXN9BxhhfquL0OzoQXg3spLYymL5rm29uPeI3KEXRaZQ9zg==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.9.tgz", + "integrity": "sha512-3VPLbcCuqji4IFTclNUtGdp9v7g+nspWdiCUbK3+iPMjJCZ6LEhn1ts626bWLOn0GiDb6j+uqGvPpqLnY7pBgw==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + } + } + }, + "@octokit/plugin-enterprise-rest": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz", + "integrity": "sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", + "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz", + "integrity": "sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==", + "dev": true + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", + "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1", + "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/request": { + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.10.tgz", + "integrity": "sha512-egA49HkqEORVGDZGav1mh+VD+7uLgOxtn5oODj6guJk0HCy+YBSYapFkSLFgeYj3Fr18ZULKGURkjyhkAChylw==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^5.0.0", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "@octokit/request-error": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.3.tgz", + "integrity": "sha512-GgD5z8Btm301i2zfvJLk/mkhvGCdjQ7wT8xF9ov5noQY8WbKZDH9cOBqXzoeKd1mLr1xH2FwbtGso135zGBgTA==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.1", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + } + } + }, + "@octokit/request-error": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", + "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/rest": { + "version": "16.43.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", + "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/plugin-paginate-rest": "^1.1.1", + "@octokit/plugin-request-log": "^1.0.0", + "@octokit/plugin-rest-endpoint-methods": "2.4.0", + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + } + }, + "@octokit/types": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", + "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "26.0.15", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", + "integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/node": { + "version": "14.14.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", + "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz", + "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/yargs": { + "version": "15.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", + "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@zkochan/cmd-shim": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", + "integrity": "sha512-o8l0+x7C7sMZU3v9GuJIAU10qQLtwR1dtRQIOmlNMtyaqhmpXOzx1HWiYoWfmmf9HHZoAkXpc9TM9PQYF9d4Jg==", + "dev": true, + "requires": { + "is-windows": "^1.0.0", + "mkdirp-promise": "^5.0.1", + "mz": "^2.5.0" + } + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.1.0.tgz", + "integrity": "sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz", + "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", + "dev": true + }, + "byte-size": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", + "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "@lerna/child-process": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.16.5.tgz", - "integrity": "sha512-vdcI7mzei9ERRV4oO8Y1LHBZ3A5+ampRKg1wq5nutLsUA4mEBN6H7JqjWOMY9xZemv6+kATm2ofjJ3lW5TszQg==", + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { - "chalk": "^2.3.1", - "execa": "^1.0.0", - "strong-log-transformer": "^2.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" } }, - "@lerna/clean": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.21.0.tgz", - "integrity": "sha512-b/L9l+MDgE/7oGbrav6rG8RTQvRiZLO1zTcG17zgJAAuhlsPxJExMlh2DFwJEVi2les70vMhHfST3Ue1IMMjpg==", + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", "dev": true, "requires": { - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/prompt": "3.18.5", - "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.16.5", - "p-map": "^2.1.0", - "p-map-series": "^1.0.0", - "p-waterfall": "^1.0.0" + "mimic-response": "^1.0.0" } }, - "@lerna/cli": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.18.5.tgz", - "integrity": "sha512-erkbxkj9jfc89vVs/jBLY/fM0I80oLmJkFUV3Q3wk9J3miYhP14zgVEBsPZY68IZlEjT6T3Xlq2xO1AVaatHsA==", + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "@lerna/global-options": "3.13.0", - "dedent": "^0.7.0", - "npmlog": "^4.1.2", - "yargs": "^14.2.2" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, - "@lerna/collect-uncommitted": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.16.5.tgz", - "integrity": "sha512-ZgqnGwpDZiWyzIQVZtQaj9tRizsL4dUOhuOStWgTAw1EMe47cvAY2kL709DzxFhjr6JpJSjXV5rZEAeU3VE0Hg==", + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "chalk": "^2.3.1", - "figgy-pudding": "^3.5.1", - "npmlog": "^4.1.2" + "color-name": "1.1.3" } }, - "@lerna/collect-updates": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.20.0.tgz", - "integrity": "sha512-qBTVT5g4fupVhBFuY4nI/3FSJtQVcDh7/gEPOpRxoXB/yCSnT38MFHXWl+y4einLciCjt/+0x6/4AG80fjay2Q==", + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "columnify": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", + "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/describe-ref": "3.16.5", - "minimatch": "^3.0.4", - "npmlog": "^4.1.2", - "slash": "^2.0.0" + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" } }, - "@lerna/command": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.21.0.tgz", - "integrity": "sha512-T2bu6R8R3KkH5YoCKdutKv123iUgUbW8efVjdGCDnCMthAQzoentOJfDeodBwn0P2OqCl3ohsiNVtSn9h78fyQ==", + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/package-graph": "3.18.5", - "@lerna/project": "3.21.0", - "@lerna/validation-error": "3.13.0", - "@lerna/write-log-file": "3.13.0", - "clone-deep": "^4.0.1", - "dedent": "^0.7.0", - "execa": "^1.0.0", - "is-ci": "^2.0.0", - "npmlog": "^4.1.2" + "delayed-stream": "~1.0.0" } }, - "@lerna/conventional-commits": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.22.0.tgz", - "integrity": "sha512-z4ZZk1e8Mhz7+IS8NxHr64wyklHctCJyWpJKEZZPJiLFJ8yKto/x38O80R10pIzC0rr8Sy/OsjSH4bl0TbbgqA==", + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "requires": { - "@lerna/validation-error": "3.13.0", - "conventional-changelog-angular": "^5.0.3", - "conventional-changelog-core": "^3.1.6", - "conventional-recommended-bump": "^5.0.0", - "fs-extra": "^8.1.0", - "get-stream": "^4.0.0", - "lodash.template": "^4.5.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "pify": "^4.0.1", - "semver": "^6.2.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + }, + "dependencies": { + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } } }, - "@lerna/create": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.22.0.tgz", - "integrity": "sha512-MdiQQzCcB4E9fBF1TyMOaAEz9lUjIHp1Ju9H7f3lXze5JK6Fl5NYkouAvsLgY6YSIhXMY8AHW2zzXeBDY4yWkw==", + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "@evocateur/pacote": "^9.6.3", - "@lerna/child-process": "3.16.5", - "@lerna/command": "3.21.0", - "@lerna/npm-conf": "3.16.0", - "@lerna/validation-error": "3.13.0", - "camelcase": "^5.0.0", - "dedent": "^0.7.0", - "fs-extra": "^8.1.0", - "globby": "^9.2.0", - "init-package-json": "^1.10.3", - "npm-package-arg": "^6.1.0", - "p-reduce": "^1.0.0", - "pify": "^4.0.1", - "semver": "^6.2.0", - "slash": "^2.0.0", - "validate-npm-package-license": "^3.0.3", - "validate-npm-package-name": "^3.0.0", - "whatwg-url": "^7.0.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, - "@lerna/create-symlink": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.16.2.tgz", - "integrity": "sha512-pzXIJp6av15P325sgiIRpsPXLFmkisLhMBCy4764d+7yjf2bzrJ4gkWVMhsv4AdF0NN3OyZ5jjzzTtLNqfR+Jw==", + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, "requires": { - "@zkochan/cmd-shim": "^3.1.0", - "fs-extra": "^8.1.0", - "npmlog": "^4.1.2" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "@lerna/describe-ref": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.16.5.tgz", - "integrity": "sha512-c01+4gUF0saOOtDBzbLMFOTJDHTKbDFNErEY6q6i9QaXuzy9LNN62z+Hw4acAAZuJQhrVWncVathcmkkjvSVGw==", + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "npmlog": "^4.1.2" + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } } }, - "@lerna/diff": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.21.0.tgz", - "integrity": "sha512-5viTR33QV3S7O+bjruo1SaR40m7F2aUHJaDAC7fL9Ca6xji+aw1KFkpCtVlISS0G8vikUREGMJh+c/VMSc8Usw==", + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/command": "3.21.0", - "@lerna/validation-error": "3.13.0", - "npmlog": "^4.1.2" + "compare-func": "^2.0.0", + "q": "^1.5.1" } }, - "@lerna/exec": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.21.0.tgz", - "integrity": "sha512-iLvDBrIE6rpdd4GIKTY9mkXyhwsJ2RvQdB9ZU+/NhR3okXfqKc6py/24tV111jqpXTtZUW6HNydT4dMao2hi1Q==", + "conventional-changelog-core": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", + "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/profiler": "3.20.0", - "@lerna/run-topologically": "3.18.5", - "@lerna/validation-error": "3.13.0", - "p-map": "^2.1.0" + "conventional-changelog-writer": "^4.0.6", + "conventional-commits-parser": "^3.0.3", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "2.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^2.0.3", + "lodash": "^4.2.1", + "normalize-package-data": "^2.3.5", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^3.0.0" + }, + "dependencies": { + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } } }, - "@lerna/filter-options": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.20.0.tgz", - "integrity": "sha512-bmcHtvxn7SIl/R9gpiNMVG7yjx7WyT0HSGw34YVZ9B+3xF/83N3r5Rgtjh4hheLZ+Q91Or0Jyu5O3Nr+AwZe2g==", + "conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz", + "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==", "dev": true, "requires": { - "@lerna/collect-updates": "3.20.0", - "@lerna/filter-packages": "3.18.0", - "dedent": "^0.7.0", - "figgy-pudding": "^3.5.1", - "npmlog": "^4.1.2" + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + } } }, - "@lerna/filter-packages": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.18.0.tgz", - "integrity": "sha512-6/0pMM04bCHNATIOkouuYmPg6KH3VkPCIgTfQmdkPJTullERyEQfNUKikrefjxo1vHOoCACDpy65JYyKiAbdwQ==", + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", "dev": true, "requires": { - "@lerna/validation-error": "3.13.0", - "multimatch": "^3.0.0", - "npmlog": "^4.1.2" + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" } }, - "@lerna/get-npm-exec-opts": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.13.0.tgz", - "integrity": "sha512-Y0xWL0rg3boVyJk6An/vurKzubyJKtrxYv2sj4bB8Mc5zZ3tqtv0ccbOkmkXKqbzvNNF7VeUt1OJ3DRgtC/QZw==", + "conventional-commits-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz", + "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^2.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + } } }, - "@lerna/get-packed": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.16.0.tgz", - "integrity": "sha512-AjsFiaJzo1GCPnJUJZiTW6J1EihrPkc2y3nMu6m3uWFxoleklsSCyImumzVZJssxMi3CPpztj8LmADLedl9kXw==", + "conventional-recommended-bump": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-5.0.1.tgz", + "integrity": "sha512-RVdt0elRcCxL90IrNP0fYCpq1uGt2MALko0eyeQ+zQuDVWtMGAy9ng6yYn3kax42lCj9+XBxQ8ZN6S9bdKxDhQ==", "dev": true, "requires": { - "fs-extra": "^8.1.0", - "ssri": "^6.0.1", - "tar": "^4.4.8" + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.1.1", + "conventional-commits-filter": "^2.0.2", + "conventional-commits-parser": "^3.0.3", + "git-raw-commits": "2.0.0", + "git-semver-tags": "^2.0.3", + "meow": "^4.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } } }, - "@lerna/github-client": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.22.0.tgz", - "integrity": "sha512-O/GwPW+Gzr3Eb5bk+nTzTJ3uv+jh5jGho9BOqKlajXaOkMYGBELEAqV5+uARNGWZFvYAiF4PgqHb6aCUu7XdXg==", + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@octokit/plugin-enterprise-rest": "^6.0.1", - "@octokit/rest": "^16.28.4", - "git-url-parse": "^11.1.2", - "npmlog": "^4.1.2" + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, - "@lerna/gitlab-client": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-3.15.0.tgz", - "integrity": "sha512-OsBvRSejHXUBMgwWQqNoioB8sgzL/Pf1pOUhHKtkiMl6aAWjklaaq5HPMvTIsZPfS6DJ9L5OK2GGZuooP/5c8Q==", + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { - "node-fetch": "^2.5.0", - "npmlog": "^4.1.2", - "whatwg-url": "^7.0.0" + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" } }, - "@lerna/global-options": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.13.0.tgz", - "integrity": "sha512-SlZvh1gVRRzYLVluz9fryY1nJpZ0FHDGB66U9tFfvnnxmueckRQxLopn3tXj3NU1kc3QANT2I5BsQkOqZ4TEFQ==", + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "@lerna/has-npm-version": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.5.tgz", - "integrity": "sha512-WL7LycR9bkftyqbYop5rEGJ9sRFIV55tSGmbN1HLrF9idwOCD7CLrT64t235t3t4O5gehDnwKI5h2U3oxTrF8Q==", - "dev": true, - "requires": { - "@lerna/child-process": "3.16.5", - "semver": "^6.2.0" - } - }, - "@lerna/import": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.22.0.tgz", - "integrity": "sha512-uWOlexasM5XR6tXi4YehODtH9Y3OZrFht3mGUFFT3OIl2s+V85xIGFfqFGMTipMPAGb2oF1UBLL48kR43hRsOg==", - "dev": true, - "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/command": "3.21.0", - "@lerna/prompt": "3.18.5", - "@lerna/pulse-till-done": "3.13.0", - "@lerna/validation-error": "3.13.0", - "dedent": "^0.7.0", - "fs-extra": "^8.1.0", - "p-map-series": "^1.0.0" - } - }, - "@lerna/info": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/info/-/info-3.21.0.tgz", - "integrity": "sha512-0XDqGYVBgWxUquFaIptW2bYSIu6jOs1BtkvRTWDDhw4zyEdp6q4eaMvqdSap1CG+7wM5jeLCi6z94wS0AuiuwA==", - "dev": true, - "requires": { - "@lerna/command": "3.21.0", - "@lerna/output": "3.13.0", - "envinfo": "^7.3.1" - } - }, - "@lerna/init": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.21.0.tgz", - "integrity": "sha512-6CM0z+EFUkFfurwdJCR+LQQF6MqHbYDCBPyhu/d086LRf58GtYZYj49J8mKG9ktayp/TOIxL/pKKjgLD8QBPOg==", - "dev": true, - "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/command": "3.21.0", - "fs-extra": "^8.1.0", - "p-map": "^2.1.0", - "write-json-file": "^3.2.0" - } - }, - "@lerna/link": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.21.0.tgz", - "integrity": "sha512-tGu9GxrX7Ivs+Wl3w1+jrLi1nQ36kNI32dcOssij6bg0oZ2M2MDEFI9UF2gmoypTaN9uO5TSsjCFS7aR79HbdQ==", - "dev": true, - "requires": { - "@lerna/command": "3.21.0", - "@lerna/package-graph": "3.18.5", - "@lerna/symlink-dependencies": "3.17.0", - "p-map": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@lerna/list": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.21.0.tgz", - "integrity": "sha512-KehRjE83B1VaAbRRkRy6jLX1Cin8ltsrQ7FHf2bhwhRHK0S54YuA6LOoBnY/NtA8bHDX/Z+G5sMY78X30NS9tg==", - "dev": true, - "requires": { - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/listable": "3.18.5", - "@lerna/output": "3.13.0" - } + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, - "@lerna/listable": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.18.5.tgz", - "integrity": "sha512-Sdr3pVyaEv5A7ZkGGYR7zN+tTl2iDcinryBPvtuv20VJrXBE8wYcOks1edBTcOWsPjCE/rMP4bo1pseyk3UTsg==", + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "requires": { - "@lerna/query-graph": "3.18.5", - "chalk": "^2.3.1", - "columnify": "^1.5.4" + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" } }, - "@lerna/log-packed": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.16.0.tgz", - "integrity": "sha512-Fp+McSNBV/P2mnLUYTaSlG8GSmpXM7krKWcllqElGxvAqv6chk2K3c2k80MeVB4WvJ9tRjUUf+i7HUTiQ9/ckQ==", + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "byte-size": "^5.0.1", - "columnify": "^1.5.4", - "has-unicode": "^2.0.1", - "npmlog": "^4.1.2" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, - "@lerna/npm-conf": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.16.0.tgz", - "integrity": "sha512-HbO3DUrTkCAn2iQ9+FF/eisDpWY5POQAOF1m7q//CZjdC2HSW3UYbKEGsSisFxSfaF9Z4jtrV+F/wX6qWs3CuA==", - "dev": true, - "requires": { - "config-chain": "^1.1.11", - "pify": "^4.0.1" - } + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true }, - "@lerna/npm-dist-tag": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.5.tgz", - "integrity": "sha512-xw0HDoIG6HreVsJND9/dGls1c+lf6vhu7yJoo56Sz5bvncTloYGLUppIfDHQr4ZvmPCK8rsh0euCVh2giPxzKQ==", - "dev": true, - "requires": { - "@evocateur/npm-registry-fetch": "^4.0.0", - "@lerna/otplease": "3.18.5", - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2" - } + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true }, - "@lerna/npm-install": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.5.tgz", - "integrity": "sha512-hfiKk8Eku6rB9uApqsalHHTHY+mOrrHeWEs+gtg7+meQZMTS3kzv4oVp5cBZigndQr3knTLjwthT/FX4KvseFg==", + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/get-npm-exec-opts": "3.13.0", - "fs-extra": "^8.1.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "signal-exit": "^3.0.2", - "write-pkg": "^3.1.0" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, - "@lerna/npm-publish": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.18.5.tgz", - "integrity": "sha512-3etLT9+2L8JAx5F8uf7qp6iAtOLSMj+ZYWY6oUgozPi/uLqU0/gsMsEXh3F0+YVW33q0M61RpduBoAlOOZnaTg==", - "dev": true, - "requires": { - "@evocateur/libnpmpublish": "^1.2.2", - "@lerna/otplease": "3.18.5", - "@lerna/run-lifecycle": "3.16.2", - "figgy-pudding": "^3.5.1", - "fs-extra": "^8.1.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "pify": "^4.0.1", - "read-package-json": "^2.0.13" + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" } }, - "@lerna/npm-run-script": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.16.5.tgz", - "integrity": "sha512-1asRi+LjmVn3pMjEdpqKJZFT/3ZNpb+VVeJMwrJaV/3DivdNg7XlPK9LTrORuKU4PSvhdEZvJmSlxCKyDpiXsQ==", + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dargs": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "@lerna/get-npm-exec-opts": "3.13.0", - "npmlog": "^4.1.2" + "number-is-nan": "^1.0.0" } }, - "@lerna/otplease": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/otplease/-/otplease-3.18.5.tgz", - "integrity": "sha512-S+SldXAbcXTEDhzdxYLU0ZBKuYyURP/ND2/dK6IpKgLxQYh/z4ScljPDMyKymmEvgiEJmBsPZAAPfmNPEzxjog==", + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "@lerna/prompt": "3.18.5", - "figgy-pudding": "^3.5.1" + "assert-plus": "^1.0.0" } }, - "@lerna/output": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.13.0.tgz", - "integrity": "sha512-7ZnQ9nvUDu/WD+bNsypmPG5MwZBwu86iRoiW6C1WBuXXDxM5cnIAC1m2WxHeFnjyMrYlRXM9PzOQ9VDD+C15Rg==", + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "dependencies": { + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + } } }, - "@lerna/pack-directory": { - "version": "3.16.4", - "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.16.4.tgz", - "integrity": "sha512-uxSF0HZeGyKaaVHz5FroDY9A5NDDiCibrbYR6+khmrhZtY0Bgn6hWq8Gswl9iIlymA+VzCbshWIMX4o2O8C8ng==", + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "@lerna/get-packed": "3.16.0", - "@lerna/package": "3.16.0", - "@lerna/run-lifecycle": "3.16.2", - "figgy-pudding": "^3.5.1", - "npm-packlist": "^1.4.4", - "npmlog": "^4.1.2", - "tar": "^4.4.10", - "temp-write": "^3.4.0" + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, - "@lerna/package": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.16.0.tgz", - "integrity": "sha512-2lHBWpaxcBoiNVbtyLtPUuTYEaB/Z+eEqRS9duxpZs6D+mTTZMNy6/5vpEVSCBmzvdYpyqhqaYjjSLvjjr5Riw==", + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", "dev": true, "requires": { - "load-json-file": "^5.3.0", - "npm-package-arg": "^6.1.0", - "write-pkg": "^3.1.0" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } } }, - "@lerna/package-graph": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.18.5.tgz", - "integrity": "sha512-8QDrR9T+dBegjeLr+n9WZTVxUYUhIUjUgZ0gvNxUBN8S1WB9r6H5Yk56/MVaB64tA3oGAN9IIxX6w0WvTfFudA==", + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "requires": { - "@lerna/prerelease-id-from-version": "3.16.0", - "@lerna/validation-error": "3.13.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "semver": "^6.2.0" + "mimic-response": "^1.0.0" } }, - "@lerna/prerelease-id-from-version": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-3.16.0.tgz", - "integrity": "sha512-qZyeUyrE59uOK8rKdGn7jQz+9uOpAaF/3hbslJVFL1NqF9ELDTqjCPXivuejMX/lN4OgD6BugTO4cR7UTq/sZA==", + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "semver": "^6.2.0" + "clone": "^1.0.2" } }, - "@lerna/profiler": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@lerna/profiler/-/profiler-3.20.0.tgz", - "integrity": "sha512-bh8hKxAlm6yu8WEOvbLENm42i2v9SsR4WbrCWSbsmOElx3foRnMlYk7NkGECa+U5c3K4C6GeBbwgqs54PP7Ljg==", + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1", - "fs-extra": "^8.1.0", - "npmlog": "^4.1.2", - "upath": "^1.2.0" + "object-keys": "^1.0.12" } }, - "@lerna/project": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.21.0.tgz", - "integrity": "sha512-xT1mrpET2BF11CY32uypV2GPtPVm6Hgtha7D81GQP9iAitk9EccrdNjYGt5UBYASl4CIDXBRxwmTTVGfrCx82A==", + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "@lerna/package": "3.16.0", - "@lerna/validation-error": "3.13.0", - "cosmiconfig": "^5.1.0", - "dedent": "^0.7.0", - "dot-prop": "^4.2.0", - "glob-parent": "^5.0.0", - "globby": "^9.2.0", - "load-json-file": "^5.3.0", - "npmlog": "^4.1.2", - "p-map": "^2.1.0", - "resolve-from": "^4.0.0", - "write-json-file": "^3.2.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, - "@lerna/prompt": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/prompt/-/prompt-3.18.5.tgz", - "integrity": "sha512-rkKj4nm1twSbBEb69+Em/2jAERK8htUuV8/xSjN0NPC+6UjzAwY52/x9n5cfmpa9lyKf/uItp7chCI7eDmNTKQ==", + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", "dev": true, "requires": { - "inquirer": "^6.2.0", - "npmlog": "^4.1.2" + "asap": "^2.0.0", + "wrappy": "1" } }, - "@lerna/publish": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.22.1.tgz", - "integrity": "sha512-PG9CM9HUYDreb1FbJwFg90TCBQooGjj+n/pb3gw/eH5mEDq0p8wKdLFe0qkiqUkm/Ub5C8DbVFertIo0Vd0zcw==", + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", "dev": true, "requires": { - "@evocateur/libnpmaccess": "^3.1.2", - "@evocateur/npm-registry-fetch": "^4.0.0", - "@evocateur/pacote": "^9.6.3", - "@lerna/check-working-tree": "3.16.5", - "@lerna/child-process": "3.16.5", - "@lerna/collect-updates": "3.20.0", - "@lerna/command": "3.21.0", - "@lerna/describe-ref": "3.16.5", - "@lerna/log-packed": "3.16.0", - "@lerna/npm-conf": "3.16.0", - "@lerna/npm-dist-tag": "3.18.5", - "@lerna/npm-publish": "3.18.5", - "@lerna/otplease": "3.18.5", - "@lerna/output": "3.13.0", - "@lerna/pack-directory": "3.16.4", - "@lerna/prerelease-id-from-version": "3.16.0", - "@lerna/prompt": "3.18.5", - "@lerna/pulse-till-done": "3.13.0", - "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-topologically": "3.18.5", - "@lerna/validation-error": "3.13.0", - "@lerna/version": "3.22.1", - "figgy-pudding": "^3.5.1", - "fs-extra": "^8.1.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "p-finally": "^1.0.0", - "p-map": "^2.1.0", - "p-pipe": "^1.2.0", - "semver": "^6.2.0" + "path-type": "^3.0.0" } }, - "@lerna/pulse-till-done": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-3.13.0.tgz", - "integrity": "sha512-1SOHpy7ZNTPulzIbargrgaJX387csN7cF1cLOGZiJQA6VqnS5eWs2CIrG8i8wmaUavj2QlQ5oEbRMVVXSsGrzA==", + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } } }, - "@lerna/query-graph": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.18.5.tgz", - "integrity": "sha512-50Lf4uuMpMWvJ306be3oQDHrWV42nai9gbIVByPBYJuVW8dT8O8pA3EzitNYBUdLL9/qEVbrR0ry1HD7EXwtRA==", + "dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "dev": true, "requires": { - "@lerna/package-graph": "3.18.5", - "figgy-pudding": "^3.5.1" + "is-obj": "^1.0.0" } }, - "@lerna/resolve-symlink": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.16.0.tgz", - "integrity": "sha512-Ibj5e7njVHNJ/NOqT4HlEgPFPtPLWsO7iu59AM5bJDcAJcR96mLZ7KGVIsS2tvaO7akMEJvt2P+ErwCdloG3jQ==", + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, "requires": { - "fs-extra": "^8.1.0", - "npmlog": "^4.1.2", - "read-cmd-shim": "^1.0.1" + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, - "@lerna/rimraf-dir": { - "version": "3.16.5", - "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.16.5.tgz", - "integrity": "sha512-bQlKmO0pXUsXoF8lOLknhyQjOZsCc0bosQDoX4lujBXSWxHVTg1VxURtWf2lUjz/ACsJVDfvHZbDm8kyBk5okA==", + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "requires": { - "@lerna/child-process": "3.16.5", - "npmlog": "^4.1.2", - "path-exists": "^3.0.0", - "rimraf": "^2.6.2" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "@lerna/run": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.21.0.tgz", - "integrity": "sha512-fJF68rT3veh+hkToFsBmUJ9MHc9yGXA7LSDvhziAojzOb0AI/jBDp6cEcDQyJ7dbnplba2Lj02IH61QUf9oW0Q==", + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "requires": { - "@lerna/command": "3.21.0", - "@lerna/filter-options": "3.20.0", - "@lerna/npm-run-script": "3.16.5", - "@lerna/output": "3.13.0", - "@lerna/profiler": "3.20.0", - "@lerna/run-topologically": "3.18.5", - "@lerna/timer": "3.13.0", - "@lerna/validation-error": "3.13.0", - "p-map": "^2.1.0" + "iconv-lite": "^0.6.2" } }, - "@lerna/run-lifecycle": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.16.2.tgz", - "integrity": "sha512-RqFoznE8rDpyyF0rOJy3+KjZCeTkO8y/OB9orPauR7G2xQ7PTdCpgo7EO6ZNdz3Al+k1BydClZz/j78gNCmL2A==", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { - "@lerna/npm-conf": "3.16.0", - "figgy-pudding": "^3.5.1", - "npm-lifecycle": "^3.1.2", - "npmlog": "^4.1.2" + "once": "^1.4.0" } }, - "@lerna/run-topologically": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.18.5.tgz", - "integrity": "sha512-6N1I+6wf4hLOnPW+XDZqwufyIQ6gqoPfHZFkfWlvTQ+Ue7CuF8qIVQ1Eddw5HKQMkxqN10thKOFfq/9NQZ4NUg==", + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "envinfo": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", + "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "@lerna/query-graph": "3.18.5", - "figgy-pudding": "^3.5.1", - "p-queue": "^4.0.0" + "is-arrayish": "^0.2.1" } }, - "@lerna/symlink-binary": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.17.0.tgz", - "integrity": "sha512-RLpy9UY6+3nT5J+5jkM5MZyMmjNHxZIZvXLV+Q3MXrf7Eaa1hNqyynyj4RO95fxbS+EZc4XVSk25DGFQbcRNSQ==", + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { - "@lerna/create-symlink": "3.16.2", - "@lerna/package": "3.16.0", - "fs-extra": "^8.1.0", - "p-map": "^2.1.0" + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, - "@lerna/symlink-dependencies": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.17.0.tgz", - "integrity": "sha512-KmjU5YT1bpt6coOmdFueTJ7DFJL4H1w5eF8yAQ2zsGNTtZ+i5SGFBWpb9AQaw168dydc3s4eu0W0Sirda+F59Q==", + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { - "@lerna/create-symlink": "3.16.2", - "@lerna/resolve-symlink": "3.16.0", - "@lerna/symlink-binary": "3.17.0", - "fs-extra": "^8.1.0", - "p-finally": "^1.0.0", - "p-map": "^2.1.0", - "p-map-series": "^1.0.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, - "@lerna/timer": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/timer/-/timer-3.13.0.tgz", - "integrity": "sha512-RHWrDl8U4XNPqY5MQHkToWS9jHPnkLZEt5VD+uunCKTfzlxGnRCr3/zVr8VGy/uENMYpVP3wJa4RKGY6M0vkRw==", + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, - "@lerna/validation-error": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-3.13.0.tgz", - "integrity": "sha512-SiJP75nwB8GhgwLKQfdkSnDufAaCbkZWJqEDlKOUPUvVOplRGnfL+BPQZH5nvq2BYSRXsksXWZ4UHVnQZI/HYA==", + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "npmlog": "^4.1.2" + "es6-promise": "^4.0.3" } }, - "@lerna/version": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.22.1.tgz", - "integrity": "sha512-PSGt/K1hVqreAFoi3zjD0VEDupQ2WZVlVIwesrE5GbrL2BjXowjCsTDPqblahDUPy0hp6h7E2kG855yLTp62+g==", + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, "requires": { - "@lerna/check-working-tree": "3.16.5", - "@lerna/child-process": "3.16.5", - "@lerna/collect-updates": "3.20.0", - "@lerna/command": "3.21.0", - "@lerna/conventional-commits": "3.22.0", - "@lerna/github-client": "3.22.0", - "@lerna/gitlab-client": "3.15.0", - "@lerna/output": "3.13.0", - "@lerna/prerelease-id-from-version": "3.16.0", - "@lerna/prompt": "3.18.5", - "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-topologically": "3.18.5", - "@lerna/validation-error": "3.13.0", - "chalk": "^2.3.1", - "dedent": "^0.7.0", - "load-json-file": "^5.3.0", - "minimatch": "^3.0.4", - "npmlog": "^4.1.2", - "p-map": "^2.1.0", - "p-pipe": "^1.2.0", - "p-reduce": "^1.0.0", - "p-waterfall": "^1.0.0", - "semver": "^6.2.0", - "slash": "^2.0.0", - "temp-write": "^3.4.0", - "write-json-file": "^3.2.0" + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, - "@lerna/write-log-file": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-3.13.0.tgz", - "integrity": "sha512-RibeMnDPvlL8bFYW5C8cs4mbI3AHfQef73tnJCQ/SgrXZHehmHnsyWUiE7qDQCAo+B1RfTapvSyFF69iPj326A==", + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", "dev": true, "requires": { - "npmlog": "^4.1.2", - "write-file-atomic": "^2.3.0" + "get-stdin": "^6.0.0" } }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + }, + "dependencies": { + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + } } }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, - "@octokit/auth-token": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.3.tgz", - "integrity": "sha512-fdGoOQ3kQJh+hrilc0Plg50xSfaCKOeYN9t6dpJKXN9BxhhfquL0OzoQXg3spLYymL5rm29uPeI3KEXRaZQ9zg==", + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "@octokit/types": "^5.0.0" + "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" } }, - "@octokit/endpoint": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.9.tgz", - "integrity": "sha512-3VPLbcCuqji4IFTclNUtGdp9v7g+nspWdiCUbK3+iPMjJCZ6LEhn1ts626bWLOn0GiDb6j+uqGvPpqLnY7pBgw==", + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "@octokit/types": "^5.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } }, - "@octokit/plugin-enterprise-rest": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz", - "integrity": "sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==", - "dev": true - }, - "@octokit/plugin-paginate-rest": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", - "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", "dev": true, "requires": { - "@octokit/types": "^2.0.1" + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" }, "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/node": ">= 8" + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, - "@octokit/plugin-request-log": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz", - "integrity": "sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==", + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", - "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "@octokit/types": "^2.0.1", - "deprecation": "^2.3.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "@types/node": ">= 8" + "is-plain-object": "^2.0.4" } } } }, - "@octokit/request": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.10.tgz", - "integrity": "sha512-egA49HkqEORVGDZGav1mh+VD+7uLgOxtn5oODj6guJk0HCy+YBSYapFkSLFgeYj3Fr18ZULKGURkjyhkAChylw==", + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^5.0.0", - "deprecation": "^2.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "once": "^1.4.0", - "universal-user-agent": "^6.0.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "dependencies": { - "@octokit/request-error": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.3.tgz", - "integrity": "sha512-GgD5z8Btm301i2zfvJLk/mkhvGCdjQ7wT8xF9ov5noQY8WbKZDH9cOBqXzoeKd1mLr1xH2FwbtGso135zGBgTA==", + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "@octokit/types": "^5.0.1", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "safer-buffer": ">= 2.1.2 < 3" } - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true } } }, - "@octokit/request-error": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", - "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "@types/node": ">= 8" + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } }, - "@octokit/rest": { - "version": "16.43.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", - "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/plugin-paginate-rest": "^1.1.1", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "2.4.0", - "@octokit/request": "^5.2.0", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^2.0.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", - "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/chai": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", - "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", - "dev": true - }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", - "dev": true - }, - "@types/mocha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", - "integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==", - "dev": true - }, - "@types/node": { - "version": "14.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", - "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "@zkochan/cmd-shim": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", - "integrity": "sha512-o8l0+x7C7sMZU3v9GuJIAU10qQLtwR1dtRQIOmlNMtyaqhmpXOzx1HWiYoWfmmf9HHZoAkXpc9TM9PQYF9d4Jg==", + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", "dev": true, "requires": { - "is-windows": "^1.0.0", - "mkdirp-promise": "^5.0.1", - "mz": "^2.5.0" + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } } }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "bser": "2.1.1" } }, - "agentkeepalive": { + "figgy-pudding": { "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "escape-string-regexp": "^1.0.5" } }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "string-width": "^3.0.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "is-extendable": "^0.1.0" } } } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "locate-path": "^3.0.0" } }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "map-cache": "^0.2.2" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-differ": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.1.0.tgz", - "integrity": "sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w==", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "safer-buffer": "~2.1.0" + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" } }, - "assert-plus": { + "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", "dev": true }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "repeating": "^2.0.0" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "before-after-hook": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", - "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", - "dev": true - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "color-convert": "^2.0.1" + "error-ex": "^1.2.0" } }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "pinkie-promise": "^2.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "color-name": "~1.1.4" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "has-flag": "^4.0.0" + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-utf8": "^0.2.0" } - } - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", - "dev": true - }, - "byte-size": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", - "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", - "dev": true - }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "pump": "^3.0.0" + "get-stdin": "^4.0.1" } }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true } } }, - "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", "dev": true }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", "dev": true }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "pump": "^3.0.0" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "assert-plus": "^1.0.0" } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "git-raw-commits": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", + "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", "dev": true, "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "dargs": "^4.0.1", + "lodash.template": "^4.0.2", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", "dev": true, "requires": { - "fill-range": "^7.0.1" + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", "dev": true, "requires": { - "to-regex-range": "^5.0.1" + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", "dev": true, "requires": { - "is-number": "^7.0.0" + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" } - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true } } }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } } }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "git-semver-tags": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", + "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "meow": "^4.0.0", + "semver": "^6.0.0" }, "dependencies": { - "ansi-regex": { + "camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, - "is-fullwidth-code-point": { + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "map-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true } } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true + "git-up": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", + "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" + } }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "git-url-parse": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.0.tgz", + "integrity": "sha512-KlIa5jvMYLjXMQXkqpFzobsyD/V2K5DRHl5OAf+6oDFPlPLxrGDVQlIdI63c4/Kt6kai4kALENSALlzTGST3GQ==", "dev": true, "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "git-up": "^4.0.0" } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "ini": "^1.3.2" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "is-glob": "^4.0.1" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", "dev": true, "requires": { - "color-name": "1.1.3" + "ini": "^1.3.5" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "columnify": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", - "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", "dev": true, "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" } }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" } }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", "dev": true, "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" }, "dependencies": { - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "function-bind": "^1.1.1" } }, - "config-chain": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "is-buffer": "^1.1.5" } } } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, - "conventional-changelog-angular": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", - "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true }, - "conventional-changelog-core": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", - "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.6", - "conventional-commits-parser": "^3.0.3", - "dateformat": "^3.0.0", - "get-pkg-repo": "^1.0.0", - "git-raw-commits": "2.0.0", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.3", - "lodash": "^4.2.1", - "normalize-package-data": "^2.3.5", - "q": "^1.5.1", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0", - "through2": "^3.0.0" - }, - "dependencies": { - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } + "whatwg-encoding": "^1.0.5" } }, - "conventional-changelog-preset-loader": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", - "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "conventional-changelog-writer": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz", - "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==", + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "dev": true, "requires": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dev": true, - "requires": { - "readable-stream": "3" - } - } + "agent-base": "4", + "debug": "3.1.0" } }, - "conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, - "conventional-commits-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz", - "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==", + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^2.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dev": true, - "requires": { - "readable-stream": "3" - } - } + "agent-base": "^4.3.0", + "debug": "^3.1.0" } }, - "conventional-recommended-bump": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-5.0.1.tgz", - "integrity": "sha512-RVdt0elRcCxL90IrNP0fYCpq1uGt2MALko0eyeQ+zQuDVWtMGAy9ng6yYn3kax42lCj9+XBxQ8ZN6S9bdKxDhQ==", + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "dev": true, "requires": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^2.1.1", - "conventional-commits-filter": "^2.0.2", - "conventional-commits-parser": "^3.0.3", - "git-raw-commits": "2.0.0", - "git-semver-tags": "^2.0.3", - "meow": "^4.0.0", - "q": "^1.5.1" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" - } - }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - } - }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true - } + "ms": "^2.0.0" } }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "dev": true, "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "minimatch": "^3.0.4" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true } } }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", "dev": true }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "requires": { - "array-find-index": "^1.0.1" + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" } }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "dargs": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", - "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "init-package-json": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", + "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" }, "dependencies": { - "ms": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } } } }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "kind-of": "^3.0.2" }, "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "binary-extensions": "^2.0.0" } }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "requires": { - "clone": "^1.0.2" + "ci-info": "^2.0.0" } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has": "^1.0.3" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "kind-of": "^3.0.2" }, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-buffer": "^1.1.5" } } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", "dev": true }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, - "detect-indent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "asap": "^2.0.0", - "wrappy": "1" + "number-is-nan": "^1.0.0" } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "path-type": "^3.0.0" + "is-extglob": "^2.1.1" } }, - "dot-prop": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", "dev": true }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "requires": { - "iconv-lite": "^0.6.2" - } + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "once": "^1.4.0" + "isobject": "^3.0.1" } }, - "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", - "dev": true - }, - "envinfo": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", - "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", "dev": true }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } }, - "error-ex": { + "is-ssh": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz", + "integrity": "sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ==", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "protocols": "^1.1.0" } }, - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "has-symbols": "^1.0.1" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "text-extensions": "^1.0.0" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { - "es6-promise": "^4.0.3" + "is-docker": "^2.0.0" } }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "eslint-config-prettier": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", - "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { - "get-stdin": "^6.0.0" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" }, "dependencies": { - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "through": "2" + "semver": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "ms": "2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "find-up": "^4.0.0" } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "resolve-from": "^5.0.0" } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "ansi-regex": "^5.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "has-flag": "^4.0.0" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.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": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "dev": true, - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } } } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "genfun": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } } }, - "get-pkg-repo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", - "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "meow": "^3.3.0", - "normalize-package-data": "^2.3.0", - "parse-github-repo-url": "^1.3.0", - "through2": "^2.0.0" + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "color-name": "~1.1.4" } }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "repeating": "^2.0.0" + "has-flag": "^4.0.0" } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + } + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "color-convert": "^2.0.1" } }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "error-ex": "^1.2.0" + "color-name": "~1.1.4" } }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" } }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "to-regex-range": "^5.0.1" } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "is-number": "^7.0.0" } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + } + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "color-convert": "^2.0.1" } }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "color-name": "~1.1.4" } }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, - "get-port": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", - "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", "dev": true, "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" } }, - "git-raw-commits": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", - "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dev": true, "requires": { - "dargs": "^4.0.1", - "lodash.template": "^4.0.2", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^2.0.0" + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" + "color-convert": "^2.0.1" } }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "color-name": "~1.1.4" } }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" + "has-flag": "^4.0.0" } - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true - } - } - }, - "git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", - "dev": true, - "requires": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true } } }, - "git-semver-tags": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", - "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", "dev": true, "requires": { - "meow": "^4.0.0", - "semver": "^6.0.0" + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" + "color-convert": "^2.0.1" } }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "color-name": "~1.1.4" } }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" + "to-regex-range": "^5.0.1" } }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true - } - } - }, - "git-up": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz", - "integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==", - "dev": true, - "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^5.0.0" - } - }, - "git-url-parse": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.0.tgz", - "integrity": "sha512-KlIa5jvMYLjXMQXkqpFzobsyD/V2K5DRHl5OAf+6oDFPlPLxrGDVQlIdI63c4/Kt6kai4kALENSALlzTGST3GQ==", - "dev": true, - "requires": { - "git-up": "^4.0.0" - } - }, - "gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", - "dev": true, - "requires": { - "ini": "^1.3.2" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true - }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "dev": true, - "requires": { - "ini": "^1.3.5" - } - }, - "globby": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", - "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^1.0.2", - "dir-glob": "^2.2.2", - "fast-glob": "^2.2.6", - "glob": "^7.1.3", - "ignore": "^4.0.3", - "pify": "^4.0.1", - "slash": "^2.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "@jest/types": "^26.6.2", + "@types/node": "*" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", "dev": true }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" }, "dependencies": { - "kind-of": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "p-locate": "^4.1.0" } - } - } - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" }, "dependencies": { - "resolve-from": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.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": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "@types/node": "*", + "graceful-fs": "^4.2.4" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "init-package-json": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", - "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", "dev": true, "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "to-regex-range": "^5.0.1" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "braces": "^3.0.1", + "picomatch": "^2.0.5" } - } - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" } } } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" }, "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "has-flag": "^4.0.0" } } } }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-ssh": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz", - "integrity": "sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ==", - "dev": true, - "requires": { - "protocols": "^1.1.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", - "dev": true, - "requires": { - "text-extensions": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4771,6 +8312,85 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4807,6 +8427,15 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -4849,6 +8478,12 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "latest-version": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", @@ -4884,6 +8519,22 @@ "npmlog": "^4.1.2" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -4943,6 +8594,12 @@ "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -4964,82 +8621,22 @@ "lodash._reinterpolate": "^3.0.0", "lodash.templatesettings": "^4.0.0" } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" } }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -5113,6 +8710,15 @@ "ssri": "^6.0.0" } }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5324,6 +8930,12 @@ } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5486,228 +9098,6 @@ "mkdirp": "*" } }, - "mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "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.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, "modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -5763,12 +9153,6 @@ "thenify-all": "^1.0.0" } }, - "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", - "dev": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -5788,6 +9172,12 @@ "to-regex": "^3.0.1" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -5850,6 +9240,59 @@ } } }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true, + "optional": true + }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "optional": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "nodemon": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz", @@ -6035,6 +9478,12 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -6154,6 +9603,20 @@ "mimic-fn": "^1.0.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -6192,6 +9655,12 @@ "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -6328,6 +9797,12 @@ "protocols": "^1.4.0" } }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -6381,12 +9856,6 @@ } } }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -6429,6 +9898,15 @@ "pinkie": "^2.0.0" } }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -6444,6 +9922,12 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -6456,6 +9940,50 @@ "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6478,6 +10006,16 @@ "retry": "^0.10.0" } }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "promzard": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", @@ -6595,15 +10133,6 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6624,6 +10153,12 @@ } } }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -6833,6 +10368,12 @@ "rc": "^1.2.8" } }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", @@ -6882,6 +10423,26 @@ "uuid": "^3.3.2" } }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6973,6 +10534,12 @@ "glob": "^7.1.3" } }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -7026,6 +10593,53 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -7041,15 +10655,6 @@ "semver": "^6.3.0" } }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -7103,12 +10708,25 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -7423,6 +11041,23 @@ "figgy-pudding": "^3.5.1" } }, + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -7444,6 +11079,12 @@ } } }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, "stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -7475,6 +11116,33 @@ "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", "dev": true }, + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -7588,6 +11256,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -7597,12 +11271,6 @@ "min-indent": "^1.0.0" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -7623,6 +11291,39 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -7672,6 +11373,44 @@ "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "dev": true }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -7696,6 +11435,12 @@ "thenify": ">= 3.1.0 < 4" } }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -7721,6 +11466,18 @@ "os-tmpdir": "~1.0.2" } }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -7820,6 +11577,45 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, + "ts-jest": { + "version": "26.4.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.4.tgz", + "integrity": "sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==", + "dev": true, + "requires": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, "ts-node": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", @@ -7925,6 +11721,15 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -8226,6 +12031,25 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-to-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz", + "integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8256,6 +12080,33 @@ "extsprintf": "^1.2.0" } }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -8271,6 +12122,32 @@ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, "whatwg-url": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", @@ -8364,18 +12241,18 @@ "execa": "^1.0.0" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, - "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", - "dev": true - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -8502,12 +12379,30 @@ } } }, + "ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "dev": true + }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -8589,38 +12484,6 @@ "decamelize": "^1.2.0" } }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - } - } - }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 5c9ac88d..630e2406 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "build": "lerna run build", "watch": "lerna run watch", - "test": "mocha -r ts-node/register --exit packages/**/*.spec.ts", + "test": "jest", "new-version": "lerna version --conventional-commits --yes", "dev-publish": "lerna run build && lerna run dev-publish", "dev-push": "lerna run build && lerna run dev-push", @@ -19,15 +19,14 @@ "url": "git+https://github.com/agile-ts/agile.git" }, "devDependencies": { - "@types/chai": "^4.2.12", - "@types/mocha": "^8.0.4", + "@types/jest": "^26.0.15", "@types/node": "^14.14.7", - "chai": "^4.2.0", "eslint-config-prettier": "^6.11.0", + "jest": "^26.6.3", "lerna": "^3.22.1", - "mocha": "^8.1.1", "nodemon": "^2.0.6", "prettier": "2.1.2", + "ts-jest": "^26.4.4", "ts-node": "^8.10.2", "tsc-watch": "^4.1.0", "tslib": "^2.0.0", diff --git a/packages/api/LICENCE b/packages/api/LICENCE new file mode 100644 index 00000000..b93156fb --- /dev/null +++ b/packages/api/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present bennodev19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/api/jest.config.js b/packages/api/jest.config.js new file mode 100644 index 00000000..db1ae36a --- /dev/null +++ b/packages/api/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require("../../jest.config.base"); + +module.exports = { + ...baseConfig, + rootDir: "../..", + name: "API", +}; diff --git a/packages/core/LICENCE b/packages/core/LICENCE new file mode 100644 index 00000000..b93156fb --- /dev/null +++ b/packages/core/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present bennodev19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js new file mode 100644 index 00000000..f9bc46d1 --- /dev/null +++ b/packages/core/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require("../../jest.config.base"); + +module.exports = { + ...baseConfig, + rootDir: "../..", + name: "Core", +}; diff --git a/packages/core/src/agile.ts b/packages/core/src/agile.ts index 6062d384..e158f7bf 100644 --- a/packages/core/src/agile.ts +++ b/packages/core/src/agile.ts @@ -20,62 +20,63 @@ import { defineConfig, Logger, CreateLoggerConfigInterface, + StateConfigInterface, } from "./internal"; export class Agile { public config: AgileConfigInterface; - public runtime: Runtime; + public runtime: Runtime; // Handles assigning Values to Agile Instances public subController: SubController; // Handles subscriptions to Components public storages: Storages; // Handles permanent saving // Integrations public integrations: Integrations; // Integrated frameworks - static initialIntegrations: Integration[] = []; // External added Integrations + static initialIntegrations: Integration[] = []; // External added initial Integrations - static logger = new Logger(); + // Static Logger with default config -> will be overwritten by config of last created Agile Instance + static logger = new Logger({ + prefix: "Agile", + active: true, + level: Logger.level.WARN, + }); /** * @public * Agile - Global state and logic framework for reactive Typescript & Javascript applications * @param config - Config */ - constructor(config: AgileConfigInterface = {}) { - this.config = defineConfig(config, { + constructor(config: CreateAgileConfigInterface = {}) { + config = defineConfig(config, { localStorage: true, waitForMount: false, + logConfig: {}, }); - this.config.logConfig = defineConfig(config.logConfig, { + config.logConfig = defineConfig(config.logConfig, { prefix: "Agile", - active: false, - level: 0, + active: true, + level: Logger.level.WARN, canUseCustomStyles: true, allowedTags: ["runtime", "storage", "subscription", "multieditor"], }); + this.config = { + waitForMount: config.waitForMount as any, + }; this.integrations = new Integrations(this); this.runtime = new Runtime(this); this.subController = new SubController(this); this.storages = new Storages(this, { - localStorage: this.config.localStorage, + localStorage: config.localStorage, }); - Agile.logger = new Logger(this.config.logConfig); + // Assign customized config to Logger + Agile.logger = new Logger(config.logConfig); // Create global instance of Agile - globalBind("__agile__", this); - } - - //========================================================================================================= - // Use - //========================================================================================================= - /** - * @public - * Integrates framework into Agile - * @param integration - Integration that gets registered/integrated - */ - public use(integration: Integration) { - this.integrations.integrate(integration); - return this; + if (!globalBind("__agile__", this)) + Agile.logger.warn( + "Be careful with multiple Agile Instances in one Application!" + ); } //========================================================================================================= @@ -96,10 +97,12 @@ export class Agile { * @public * State - Class that holds one Value and causes rerender on subscribed Components * @param initialValue - Initial Value of the State - * @param key - Key/Name of the State + * @param config - Config */ - public State = (initialValue: ValueType, key?: string) => - new State(this, initialValue, key); + public State = ( + initialValue: ValueType, + config: StateConfigInterface = {} + ) => new State(this, initialValue, config); //========================================================================================================= // Collection @@ -125,8 +128,10 @@ export class Agile { public Computed = ( computeFunction: () => ComputedValueType, deps?: Array - // @ts-ignore - ) => new Computed(this, computeFunction, deps); + ) => + new Computed(this, computeFunction, { + computedDeps: deps, + }); //========================================================================================================= // Event @@ -140,6 +145,19 @@ export class Agile { config?: CreateEventConfigInterface ) => new Event(this, config); + //========================================================================================================= + // Integrate + //========================================================================================================= + /** + * @public + * Integrates framework into Agile + * @param integration - Integration that gets registered/integrated + */ + public integrate(integration: Integration) { + this.integrations.integrate(integration); + return this; + } + //========================================================================================================= // Register Storage //========================================================================================================= @@ -152,8 +170,9 @@ export class Agile { public registerStorage( storage: Storage, config: RegisterConfigInterface = {} - ): boolean { - return this.storages.register(storage, config); + ): this { + this.storages.register(storage, config); + return this; } //========================================================================================================= @@ -166,6 +185,17 @@ export class Agile { public hasIntegration(): boolean { return this.integrations.hasIntegration(); } + + //========================================================================================================= + // Has Storage + //========================================================================================================= + /** + * @public + * Checks if Agile has any registered Storage + */ + public hasStorage(): boolean { + return this.storages.hasStorage(); + } } /** @@ -173,8 +203,15 @@ export class Agile { * @param waitForMount - If Agile should wait until the component mounts * @param storageConfig - To configure Agile Storage */ -export interface AgileConfigInterface { +export interface CreateAgileConfigInterface { logConfig?: CreateLoggerConfigInterface; waitForMount?: boolean; localStorage?: boolean; } + +/** + * @param waitForMount - If Agile should wait until the component mounts + */ +export interface AgileConfigInterface { + waitForMount: boolean; +} diff --git a/packages/core/src/collection/collection.persistent.ts b/packages/core/src/collection/collection.persistent.ts index db5c21c4..540aa663 100644 --- a/packages/core/src/collection/collection.persistent.ts +++ b/packages/core/src/collection/collection.persistent.ts @@ -2,186 +2,194 @@ import { Agile, Collection, CollectionKey, + CreatePersistentConfigInterface, defineConfig, Group, GroupKey, ItemKey, Persistent, + PersistentKey, StorageKey, } from "../internal"; export class CollectionPersistent extends Persistent { public collection: () => Collection; - private defaultGroupSideEffectKey = "rebuildStorage"; - public static storageItemKeyPattern = "_${collectionKey}_item_${itemKey}"; - public static storageGroupKeyPattern = "_${collectionKey}_group_${groupKey}"; + static defaultGroupSideEffectKey = "rebuildGroupStorageValue"; + static storageItemKeyPattern = "_${collectionKey}_item_${itemKey}"; + static storageGroupKeyPattern = "_${collectionKey}_group_${groupKey}"; /** * @internal * Collection Persist Manager - Handles permanent storing of Collection Value * @param collection - Collection that gets stored - * @param key - Key of Storage property * @param config - Config */ constructor( collection: Collection, - key?: StorageKey, - config: CollectionPersistentConfigInterface = {} + config: CreatePersistentConfigInterface = {} ) { - super(collection.agileInstance()); + super(collection.agileInstance(), { + instantiate: false, + }); config = defineConfig(config, { instantiate: true, + storageKeys: [], }); this.collection = () => collection; - this.storageKeys = config.storageKeys; - if (config?.instantiate) - this.instantiatePersistent(key).then((success) => { - collection.isPersisted = success; - }); + this.instantiatePersistent({ + key: config.key, + storageKeys: config.storageKeys, + }); + + // Load/Store persisted Value/s for the first Time + if (this.ready && config.instantiate) this.initialLoading(); } //========================================================================================================= // Set Key //========================================================================================================= /** - * @public - * Sets Key/Name of Persistent + * @internal + * Updates Key/Name of Persistent * @param value - New Key/Name of Persistent */ - public async setKey(value: StorageKey) { - // If persistent isn't ready try to init it with the new Key - if (!this.ready) { - this.instantiatePersistent(value).then((success) => { - this.collection().isPersisted = success; - }); - return; - } + public async setKey(value?: StorageKey): Promise { + const oldKey = this._key; + const wasReady = this.ready; - // Check if key has changed + // Assign Key if (value === this._key) return; + this._key = value || Persistent.placeHolderKey; + + const isValid = this.validatePersistent(); - // Remove value with old Key - await this.removeValue(); + // Try to Initial Load Value if persistent wasn't ready + if (!wasReady) { + if (isValid) await this.initialLoading(); + return; + } - // Update Key - this._key = value; + // Remove value at old Key + await this.removePersistedValue(oldKey); - // Set value with new Key - await this.updateValue(); + // Assign Value to new Key + if (isValid) await this.persistValue(value); } //========================================================================================================= - // Load Value + // Initial Loading //========================================================================================================= /** * @internal - * Loads Value from Storage + * Loads/Saves Storage Value for the first Time + */ + public async initialLoading() { + super.initialLoading().then(() => { + this.collection().isPersisted = true; + }); + } + + //========================================================================================================= + // Load Persisted Value + //========================================================================================================= + /** + * @internal + * Loads Collection from Storage + * @param key - Prefix Key of Persisted Instances (default PersistentKey) * @return Success? */ - public async loadValue(): Promise { + public async loadPersistedValue(key?: PersistentKey): Promise { if (!this.ready) return false; + const _key = key || this._key; // Check if Collection is Persisted - const isPersisted = await this.agileInstance().storages.get( - this.key, - this.storageKeys && this.storageKeys[0] + const isPersisted = await this.agileInstance().storages.get( + _key, + this.defaultStorageKey ); if (!isPersisted) return false; - // Load Values into Collection + // Loads Values into Collection const loadValuesIntoCollection = async () => { - const primaryKey = this.collection().config.primaryKey; - - // Get Default Group const defaultGroup = this.collection().getGroup( this.collection().config.defaultGroupKey ); if (!defaultGroup) return false; - // Persist Default Group and instantiate it manually to await its instantiation - const groupStorageKey = CollectionPersistent.getGroupStorageKey( - defaultGroup.key, - this.collection().key - ); - defaultGroup.persist(groupStorageKey, { instantiate: false }); - defaultGroup.isPersisted = - (await defaultGroup.persistent?.instantiatePersistent( - groupStorageKey - )) || false; - - // Add sideEffect to default Group which adds and removes Items from the Storage depending on the Group Value - if (!defaultGroup.hasSideEffect(this.defaultGroupSideEffectKey)) - defaultGroup.addSideEffect(this.defaultGroupSideEffectKey, () => - this.rebuildStorageSideEffect(defaultGroup) + // Persist Default Group and load its Value manually to be 100% sure it got loaded + defaultGroup.persist({ + instantiate: false, + followCollectionPersistKeyPattern: true, + }); + if (defaultGroup.persistent?.ready) { + await defaultGroup.persistent?.initialLoading(); + defaultGroup.isPersisted = true; + } + + // Load Items into Collection + for (let itemKey of defaultGroup._value) { + const itemStorageKey = CollectionPersistent.getItemStorageKey( + itemKey, + _key ); - // Load Storage Value from Items - for (let itemKey of defaultGroup.value) { // Get Storage Value - const storageValue = await this.agileInstance().storages.get( - CollectionPersistent.getItemStorageKey( - itemKey, - this.collection().key - ), - this.storageKeys && this.storageKeys[0] + const storageValue = await this.agileInstance().storages.get( + itemStorageKey, + this.defaultStorageKey ); if (!storageValue) continue; // Collect found Storage Value this.collection().collect(storageValue); - - // Persist found Item that got created out of the Storage Value - this.collection() - .getItem(storageValue[primaryKey]) - ?.persist( - CollectionPersistent.getItemStorageKey( - itemKey, - this.collection().key - ) - ); } + return true; }; + const success = await loadValuesIntoCollection(); - await loadValuesIntoCollection(); - return true; + // Persist Collection, so that the Storage Value updates dynamically if the Collection updates + if (success) await this.persistValue(_key); + + return success; } //========================================================================================================= - // Set Value + // Persist Value //========================================================================================================= /** * @internal - * Saves/Updates Value in Storage + * Sets everything up so that the Collection gets saved in the Storage + * @param key - Prefix Key of Persisted Instances (default PersistentKey) * @return Success? */ - public async updateValue(): Promise { + public async persistValue(key?: PersistentKey): Promise { if (!this.ready) return false; - - // Set Collection to Persisted (in Storage) - this.agileInstance().storages.set(this.key, true, this.storageKeys); - - // Get default Group + const _key = key || this._key; const defaultGroup = this.collection().getGroup( this.collection().config.defaultGroupKey ); if (!defaultGroup) return false; + // Set Collection to Persisted (in Storage) + this.agileInstance().storages.set(_key, true, this.storageKeys); + // Persist default Group - defaultGroup.persist({ followCollectionPattern: true }); + if (!defaultGroup.isPersisted) + defaultGroup.persist({ followCollectionPersistKeyPattern: true }); // Add sideEffect to default Group which adds and removes Items from the Storage depending on the Group Value - if (!defaultGroup.hasSideEffect(this.defaultGroupSideEffectKey)) - defaultGroup.addSideEffect(this.defaultGroupSideEffectKey, () => - this.rebuildStorageSideEffect(defaultGroup) - ); + defaultGroup.addSideEffect( + CollectionPersistent.defaultGroupSideEffectKey, + () => this.rebuildStorageSideEffect(defaultGroup, _key) + ); // Persist Collection Items - for (let itemKey of defaultGroup.value) { + for (let itemKey of defaultGroup._value) { const item = this.collection().getItem(itemKey); const itemStorageKey = CollectionPersistent.getItemStorageKey( itemKey, - this.collection().key + _key ); item?.persist(itemStorageKey); } @@ -191,60 +199,61 @@ export class CollectionPersistent extends Persistent { } //========================================================================================================= - // Remove Value + // Remove Persisted Value //========================================================================================================= /** * @internal - * Removes Value form Storage + * Removes Collection from the Storage + * @param key - Prefix Key of Persisted Instances (default PersistentKey) * @return Success? */ - public async removeValue(): Promise { + public async removePersistedValue(key?: PersistentKey): Promise { if (!this.ready) return false; - - // Set Collection to not Persisted - this.agileInstance().storages.remove(this.key, this.storageKeys); - - // Get default Group + const _key = key || this._key; const defaultGroup = this.collection().getGroup( this.collection().config.defaultGroupKey ); if (!defaultGroup) return false; + // Set Collection to not Persisted + this.agileInstance().storages.remove(_key, this.storageKeys); + // Remove default Group from Storage - defaultGroup.persistent?.removeValue(); + defaultGroup.persistent?.removePersistedValue(); - // Remove sideEffect from default Group - defaultGroup.removeSideEffect(this.defaultGroupSideEffectKey); + // Remove Rebuild Storage sideEffect from default Group + defaultGroup.removeSideEffect( + CollectionPersistent.defaultGroupSideEffectKey + ); // Remove Collection Items from Storage - for (let itemKey of defaultGroup.value) { + for (let itemKey of defaultGroup._value) { const item = this.collection().getItem(itemKey); - item?.persistent?.removeValue(); + item?.persistent?.removePersistedValue(); } this.isPersisted = false; - return false; + return true; } //========================================================================================================= - // Validate Key + // Format Key //========================================================================================================= /** * @internal - * Validates Storage Key - * @param key - Key that gets validated + * Formats Storage Key + * @param key - Key that gets formatted */ - public validateKey(key?: StorageKey): StorageKey | null { + public formatKey(key?: StorageKey): StorageKey | undefined { const collection = this.collection(); // Get key from Collection - if (!key && collection.key) return collection.key; + if (!key && collection._key) return collection._key; - // Return null if no key found - if (!key) return null; + if (!key) return; // Set Storage Key to Collection Key if Collection has no key - if (!collection.key) collection.key = key; + if (!collection._key) collection._key = key; return key; } @@ -256,33 +265,37 @@ export class CollectionPersistent extends Persistent { * @internal * Rebuilds Storage depending on Group * @param group - Group + * @param key - Prefix Key of Persisted Instances (default PersistentKey) */ - private rebuildStorageSideEffect(group: Group) { + public rebuildStorageSideEffect(group: Group, key?: PersistentKey) { const collection = group.collection(); + const _key = key || collection.persistent?._key; - // Return if only an ItemKey got updated -> length stayed the same - if (group.previousStateValue.length === group.value.length) return; + // Return if only a ItemKey got updated + if (group.previousStateValue.length === group._value.length) return; - const addedKeys = group.value.filter( + const addedKeys = group._value.filter( (key) => !group.previousStateValue.includes(key) ); const removedKeys = group.previousStateValue.filter( - (key) => !group.value.includes(key) + (key) => !group._value.includes(key) ); // Persist Added Keys addedKeys.forEach((itemKey) => { const item = collection.getItem(itemKey); - if (!item?.isPersisted) - item?.persist( - CollectionPersistent.getItemStorageKey(itemKey, collection.key) - ); + const _itemKey = CollectionPersistent.getItemStorageKey(itemKey, _key); + if (!item) return; + if (!item.isPersisted) item.persist(_itemKey); + else item.persistent?.persistValue(_itemKey); }); // Unpersist removed Keys removedKeys.forEach((itemKey) => { const item = collection.getItem(itemKey); - if (item?.isPersisted) item?.persistent?.removeValue(); + const _itemKey = CollectionPersistent.getItemStorageKey(itemKey, _key); + if (!item) return; + if (item.isPersisted) item.persistent?.removePersistedValue(_itemKey); }); } @@ -299,14 +312,10 @@ export class CollectionPersistent extends Persistent { itemKey?: ItemKey, collectionKey?: CollectionKey ): string { - if (!itemKey) { - Agile.logger.error("Failed to build Item StorageKey"); - itemKey = "unknown"; - } - if (!collectionKey) { - Agile.logger.error("Failed to build Item StorageKey"); - collectionKey = "unknown"; - } + if (!itemKey || !collectionKey) + Agile.logger.warn("Failed to build unique Item StorageKey!"); + if (!itemKey) itemKey = "unknown"; + if (!collectionKey) collectionKey = "unknown"; return this.storageItemKeyPattern .replace("${collectionKey}", collectionKey.toString()) .replace("${itemKey}", itemKey.toString()); @@ -325,25 +334,13 @@ export class CollectionPersistent extends Persistent { groupKey?: GroupKey, collectionKey?: CollectionKey ): string { - if (!groupKey) { - Agile.logger.error("Failed to build Group StorageKey"); - groupKey = "unknown"; - } - if (!collectionKey) { - Agile.logger.error("Failed to build Group StorageKey"); - collectionKey = "unknown"; - } + if (!groupKey || !collectionKey) + Agile.logger.warn("Failed to build unique Group StorageKey!"); + if (!groupKey) groupKey = "unknown"; + if (!collectionKey) collectionKey = "unknown"; + return this.storageGroupKeyPattern .replace("${collectionKey}", collectionKey.toString()) .replace("${groupKey}", groupKey.toString()); } } - -/** - * @param instantiate - If Persistent gets instantiated - * @param storageKeys - Key/Name of Storages which gets used to persist the Collection Value (NOTE: If not passed the default Storage will be used) - */ -export interface CollectionPersistentConfigInterface { - instantiate?: boolean; - storageKeys?: StorageKey[]; -} diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index c03f1b1b..679d98a4 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -7,14 +7,17 @@ import { defineConfig, normalizeArray, Item, - StorageKey, copy, - StatePersistentConfigInterface, CollectionPersistent, + StatePersistentConfigInterface, isValidObject, + PersistentKey, + ComputedTracker, + StateRuntimeJobConfigInterface, } from "../internal"; export class Group extends State> { + static rebuildGroupSideEffectKey = "rebuildGroup"; collection: () => Collection; _output: Array = []; // Output of Group @@ -31,15 +34,15 @@ export class Group extends State> { constructor( collection: Collection, initialItems?: Array, - config?: GroupConfigInterface + config: GroupConfigInterface = {} ) { - super(collection.agileInstance(), initialItems || [], config?.key); + super(collection.agileInstance(), initialItems || [], config); this.collection = () => collection; - // Add rebuild to sideEffects so that it rebuilds the Group Output if the value changes - this.addSideEffect("buildGroup", () => this.rebuild()); + // Add rebuild to sideEffects to rebuild Group on Value Change + this.addSideEffect(Group.rebuildGroupSideEffectKey, () => this.rebuild()); - // Initial Build + // Initial Rebuild this.rebuild(); } @@ -48,25 +51,35 @@ export class Group extends State> { * Get Item Values of Group */ public get output(): Array { - // Add Group to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(this.observer); - + ComputedTracker.tracked(this.observer); return this._output; } + /** + * @public + * Set Item Values of Group + */ + public set output(value: DataType[]) { + this._output = value; + } + /** * @public * Get Items of Group */ public get items(): Array> { - // Add Group to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(this.observer); - + ComputedTracker.tracked(this.observer); return this._items.map((item) => item()); } + /** + * @public + * Set Items of Group + */ + public set items(value: Array>) { + this._items = value.map((item) => () => item); + } + //========================================================================================================= // Has //========================================================================================================= @@ -104,8 +117,9 @@ export class Group extends State> { config: GroupRemoveConfig = {} ): this { const _itemKeys = normalizeArray(itemKeys); + const notExistingItemKeysInCollection: Array = []; const notExistingItemKeys: Array = []; - let newGroupValue = copy(this.nextStateValue); // Copying nextStateValue because somehow a reference exists between nextStateValue and value + let newGroupValue = copy(this.nextStateValue); config = defineConfig(config, { background: false, }); @@ -115,27 +129,29 @@ export class Group extends State> { // Check if itemKey exists in Group if (!newGroupValue.includes(itemKey)) { Agile.logger.error( - `Couldn't find itemKey '${itemKey}' in Group!`, - this + `Couldn't find ItemKey '${itemKey}' in Group '${this._key}'!` ); + notExistingItemKeys.push(itemKey); + notExistingItemKeysInCollection.push(itemKey); return; } // Check if ItemKey exists in Collection if (!this.collection().getItem(itemKey)) - notExistingItemKeys.push(itemKey); + notExistingItemKeysInCollection.push(itemKey); // Remove ItemKey from Group newGroupValue = newGroupValue.filter((key) => key !== itemKey); }); - this.nextStateValue = newGroupValue; + + // Return if passed ItemKeys doesn't exist + if (notExistingItemKeys.length >= _itemKeys.length) return this; // If all removed ItemKeys doesn't exist in Collection -> no rerender necessary since output doesn't change - if (notExistingItemKeys.length >= _itemKeys.length) + if (notExistingItemKeysInCollection.length >= _itemKeys.length) config.background = true; - // Ingest nextStateValue into Runtime - this.ingest({ background: config.background }); + this.set(newGroupValue, { background: config.background }); return this; } @@ -151,8 +167,9 @@ export class Group extends State> { */ public add(itemKeys: ItemKey | ItemKey[], config: GroupAddConfig = {}): this { const _itemKeys = normalizeArray(itemKeys); - const notExistingItemKeys: Array = []; // ItemKeys that don't exist in Collection - let newGroupValue = copy(this.nextStateValue); // Copying nextStateValue because somehow a reference exists between nextStateValue and value + const notExistingItemKeysInCollection: Array = []; + const existingItemKeys: Array = []; + let newGroupValue = copy(this.nextStateValue); config = defineConfig(config, { method: "push", overwrite: false, @@ -165,27 +182,55 @@ export class Group extends State> { // Check if ItemKey exists in Collection if (!this.collection().getItem(itemKey)) - notExistingItemKeys.push(itemKey); + notExistingItemKeysInCollection.push(itemKey); - // Remove ItemKey from Group if it should get overwritten and exists + // Remove ItemKey from Group if it should get overwritten and already exists if (existsInGroup) { - if (config.overwrite) + if (config.overwrite) { newGroupValue = newGroupValue.filter((key) => key !== itemKey); - else return; + } else { + existingItemKeys.push(itemKey); + return; + } } // Add new ItemKey to Group newGroupValue[config.method || "push"](itemKey); }); - this.nextStateValue = newGroupValue; - // If all added ItemKeys doesn't exist in Collection -> no rerender necessary since output doesn't change - if (notExistingItemKeys.length >= _itemKeys.length) + // Return if passed ItemKeys already exist + if (existingItemKeys.length >= _itemKeys.length) return this; + + // If all added ItemKeys doesn't exist in Collection or already exist -> no rerender necessary since output doesn't change + if ( + notExistingItemKeysInCollection.concat(existingItemKeys).length >= + _itemKeys.length + ) config.background = true; - // Ingest nextStateValue into Runtime - this.ingest({ background: config.background }); + this.set(newGroupValue, { background: config.background }); + + return this; + } + //========================================================================================================= + // Replace + //========================================================================================================= + /** + * @public + * Replaces oldItemKey with newItemKey + * @param oldItemKey - Old ItemKey + * @param newItemKey - New ItemKey + * @param config - Config + */ + public replace( + oldItemKey: ItemKey, + newItemKey: ItemKey, + config: StateRuntimeJobConfigInterface = {} + ): this { + let newGroupValue = copy(this._value); + newGroupValue.splice(newGroupValue.indexOf(oldItemKey), 1, newItemKey); + this.set(newGroupValue, config); return this; } @@ -201,38 +246,46 @@ export class Group extends State> { /** * @public * Stores Group Value into Agile Storage permanently - * @param key - Storage Key (Note: not needed if Group has key/name) + * @param key - Key/Name of created Persistent (Note: Key required if Group has no set Key!) * @param config - Config */ - public persist(key?: StorageKey, config?: GroupPersistConfigInterface): this; public persist( - keyOrConfig: StorageKey | GroupPersistConfigInterface = {}, + key?: PersistentKey, + config?: GroupPersistConfigInterface + ): this; + public persist( + keyOrConfig: PersistentKey | GroupPersistConfigInterface = {}, config: GroupPersistConfigInterface = {} ): this { let _config: GroupPersistConfigInterface; - let key: StorageKey | undefined; + let key: PersistentKey | undefined; if (isValidObject(keyOrConfig)) { _config = keyOrConfig as GroupPersistConfigInterface; - key = undefined; + key = this._key; } else { _config = config || {}; - key = keyOrConfig as StorageKey; + key = keyOrConfig as PersistentKey; } _config = defineConfig(_config, { instantiate: true, followCollectionPattern: false, + storageKeys: [], }); - if (_config.followCollectionPattern) { + if (_config.followCollectionPersistKeyPattern) { key = CollectionPersistent.getGroupStorageKey( - key || this.key, - this.collection().key + key || this._key, + this.collection()._key ); } - super.persist(key, { instantiate: _config.instantiate }); + super.persist(key, { + instantiate: _config.instantiate, + storageKeys: _config.storageKeys, + }); + return this; } @@ -250,8 +303,8 @@ export class Group extends State> { // Create groupItems by finding Item at ItemKey in Collection this._value.forEach((itemKey) => { - let data = this.collection().data[itemKey]; - if (data) groupItems.push(data); + const item = this.collection().getItem(itemKey); + if (item) groupItems.push(item); else notFoundItemKeys.push(itemKey); }); @@ -261,13 +314,16 @@ export class Group extends State> { }); // Logging - if (notFoundItemKeys.length > 0) + if (notFoundItemKeys.length > 0) { Agile.logger.warn( - `Couldn't find some Items in Collection '${this.key}'`, + `Couldn't find some Items in Collection '${this.collection()._key}' (${ + this._key + })`, notFoundItemKeys ); + } - this._items = groupItems.map((item) => () => item); + this.items = groupItems; this._output = groupOutput; this.notFoundItemKeys = notFoundItemKeys; } @@ -295,9 +351,11 @@ export interface GroupRemoveConfig { /** * @param key - Key/Name of Group + * @param isPlaceholder - If Group is initially a Placeholder */ export interface GroupConfigInterface { key?: GroupKey; + isPlaceholder?: boolean; } /** @@ -305,5 +363,5 @@ export interface GroupConfigInterface { */ export interface GroupPersistConfigInterface extends StatePersistentConfigInterface { - followCollectionPattern?: boolean; + followCollectionPersistKeyPattern?: boolean; } diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index 954f5fe4..3839dedb 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -14,7 +14,7 @@ import { copy, CollectionPersistent, GroupAddConfig, - CollectionPersistentConfigInterface, + ComputedTracker, } from "../internal"; export class Collection { @@ -23,10 +23,10 @@ export class Collection { public config: CollectionConfigInterface; private initialConfig: CreateCollectionConfigInterface; - public size: number = 0; // Amount of Items stored in Collection + public size = 0; // Amount of Items stored in Collection public data: { [key: string]: Item } = {}; // Collection Data public _key?: CollectionKey; - public isPersisted: boolean = false; // If Collection can be stored in Agile Storage (-> successfully integrated persistent) + public isPersisted = false; // If Collection can be stored in Agile Storage (-> successfully integrated persistent) public persistent: CollectionPersistent | undefined; // Manages storing Collection Value into Storage public groups: { [key: string]: Group } = {}; @@ -34,7 +34,7 @@ export class Collection { /** * @public - * Class that holds a List of Objects with key and causes rerender on subscribed Components + * Collection - Class that holds a List of Objects with key and causes rerender on subscribed Components * @param agileInstance - An instance of Agile * @param config - Config */ @@ -93,16 +93,14 @@ export class Collection { public setKey(value: CollectionKey | undefined) { const oldKey = this._key; - // Update Collection Key + // Update State Key this._key = value; - // Update Key in PersistManager - if ( - value !== undefined && - this.persistent && - this.persistent.key === oldKey - ) - this.persistent.key = value; + // Update Key in Persistent (only if oldKey equal to persistentKey -> otherwise the PersistentKey got formatted and will be set where other) + if (value && this.persistent?._key === oldKey) + this.persistent?.setKey(value); + + return this; } //========================================================================================================= @@ -144,11 +142,11 @@ export class Collection { * @internal * Instantiates Groups */ - private initGroups(groups: { [key: string]: Group } | string[]) { + public initGroups(groups: { [key: string]: Group } | string[]) { if (!groups) return; let groupsObject: { [key: string]: Group } = {}; - // If groups is Array of SelectorNames transform it to Selector Object + // If groups is Array of GroupNames transform it to Group Object if (Array.isArray(groups)) { groups.forEach((groupKey) => { groupsObject[groupKey] = new Group(this, [], { @@ -164,7 +162,7 @@ export class Collection { // Set Key/Name of Group to property Name for (let key in groupsObject) - if (!groupsObject[key].key) groupsObject[key].key = key; + if (!groupsObject[key]._key) groupsObject[key].setKey(key); this.groups = groupsObject; } @@ -176,9 +174,7 @@ export class Collection { * @internal * Instantiates Selectors */ - private initSelectors( - selectors: { [key: string]: Selector } | string[] - ) { + public initSelectors(selectors: { [key: string]: Selector } | string[]) { if (!selectors) return; let selectorsObject: { [key: string]: Selector } = {}; @@ -197,7 +193,7 @@ export class Collection { // Set Key/Name of Selector to property Name for (let key in selectorsObject) - if (!selectorsObject[key].key) selectorsObject[key].key = key; + if (!selectorsObject[key]._key) selectorsObject[key].setKey(key); this.selectors = selectorsObject; } @@ -208,37 +204,34 @@ export class Collection { /** * @public * Collect Item/s - * @param items - Item/s that get collected and added to this Collection - * @param groups - Add collected Item/s to certain Groups + * @param data - Data that gets added to Collection + * @param groupKeys - Add collected Item/s to certain Groups * @param config - Config */ public collect( - items: DataType | Array, - groups?: GroupKey | Array, + data: DataType | Array, + groupKeys?: GroupKey | Array, config: CollectConfigInterface = {} ): this { - const _items = normalizeArray(items); - const groupKeys = normalizeArray(groups); + const _data = normalizeArray(data); + const _groupKeys = normalizeArray(groupKeys); const defaultGroupKey = this.config.defaultGroupKey; const primaryKey = this.config.primaryKey; config = defineConfig(config, { method: "push", background: false, patch: false, + select: false, }); // Add default GroupKey, because Items get always added to default Group - if (!groupKeys.includes(defaultGroupKey)) groupKeys.push(defaultGroupKey); + if (!_groupKeys.includes(defaultGroupKey)) _groupKeys.push(defaultGroupKey); // Create not existing Groups - groupKeys.forEach( - (groupName) => !this.groups[groupName] && this.createGroup(groupName) - ); + _groupKeys.forEach((key) => !this.groups[key] && this.createGroup(key)); - // Instantiate Items - _items.forEach((data, index) => { + _data.forEach((data, index) => { const itemKey = data[primaryKey]; - const itemExistsInCollection = !!this.data[itemKey]; // Add Item to Collection const success = this.setData(data, { @@ -247,28 +240,15 @@ export class Collection { }); if (!success) return this; - // Ingest Groups that include the ItemKey into Runtime, which than rebuilds the Group (because output of group changed) - if (!itemExistsInCollection) { - for (let groupKey in this.groups) { - const group = this.getGroup(groupKey); - if (group && group.value.includes(itemKey)) { - group.ingest({ - force: true, - background: config.background, - storage: false, - }); - } - } - } - // Add ItemKey to provided Groups - groupKeys.forEach((groupKey) => { - this.groups[groupKey].add(itemKey, { + _groupKeys.forEach((groupKey) => { + this.getGroup(groupKey)?.add(itemKey, { method: config.method, background: config.background, }); }); + if (config.select) this.createSelector(itemKey, itemKey); if (config.forEachItem) config.forEachItem(data, itemKey, index); }); @@ -290,47 +270,48 @@ export class Collection { changes: DefaultItem | DataType, config: UpdateConfigInterface = {} ): Item | undefined { - if (!this.data.hasOwnProperty(itemKey)) { - console.error( - `Agile: ItemKey '${itemKey}' doesn't exist in Collection!`, - this + const item = this.getItem(itemKey, { notExisting: true }); + const primaryKey = this.config.primaryKey; + config = defineConfig(config, { + addNewProperties: false, + background: false, + }); + + if (!item) { + Agile.logger.error( + `ItemKey '${itemKey}' doesn't exist in Collection '${this._key}'!` ); return undefined; } if (!isValidObject(changes)) { - console.error(`Agile: Changes have to be an Object!`, this); + Agile.logger.error( + `You have to pass an valid Changes Object to update '${itemKey}' in '${this._key}'!` + ); return undefined; } - const item = this.data[itemKey]; - const primaryKey = this.config.primaryKey; - config = defineConfig(config, { - addNewProperties: true, - background: false, - }); - - // Merge changes into ItemValue + // Merge changes into current ItemValue const newItemValue = flatMerge(copy(item.nextStateValue), changes, { addNewProperties: config.addNewProperties, }); - const oldItemKey = item.value[primaryKey]; + const oldItemKey = item._value[primaryKey]; const newItemKey = newItemValue[primaryKey]; - const updateItemKey = oldItemKey !== newItemKey; + const updatedItemKey = oldItemKey !== newItemKey; // Apply changes to Item item.set(newItemValue, { background: config.background, - storage: !updateItemKey, // depends if the ItemKey got updated since it would get overwritten if the ItemKey/StorageKey gets updated anyway + storage: !updatedItemKey, // depends if the ItemKey got updated since it would get overwritten if the ItemKey/StorageKey gets updated anyway }); // Update ItemKey of Item - if (updateItemKey) + if (updatedItemKey) this.updateItemKey(oldItemKey, newItemKey, { background: config.background, }); - return this.data[newItemValue[primaryKey]]; + return item; } //========================================================================================================= @@ -344,65 +325,34 @@ export class Collection { */ public createGroup( groupKey: GroupKey, - initialItems?: Array + initialItems: Array = [] ): Group { - let group = this.getGroup(groupKey); + let group = this.getGroup(groupKey, { notExisting: true }); - // Create or update Group + // Check if Group already exists if (group) { - if (!group.isPlaceholder) return group; - group.set(initialItems || []); - group.isPlaceholder = false; - } else { - group = new Group(this, initialItems, { key: groupKey }); - this.groups[groupKey] = group; + if (!group.isPlaceholder) { + Agile.logger.warn(`Group with the name '${groupKey}' already exists!`); + return group; + } + group.set(initialItems, { overwrite: true }); + return group; } - if (this.groups.hasOwnProperty(groupKey)) { - console.warn(`Agile: Group with the name '${groupKey}' already exists!`); - return this.groups[groupKey]; - } + // Create Group + group = new Group(this, initialItems, { key: groupKey }); + this.groups[groupKey] = group; return group; } - //========================================================================================================= - // Create Selector - //========================================================================================================= - /** - * @public - * Creates new Selector that represents an Item of the Collection - * @param selectorKey - Name/Key of Selector - * @param itemKey - Key of Item which the Selector represents - */ - public createSelector( - selectorKey: SelectorKey, - itemKey: ItemKey - ): Selector { - let selector = this.getSelector(selectorKey); - - // Create or update Selector - if (selector) { - if (!selector.isPlaceholder) return selector; - selector.select(itemKey); - selector.isPlaceholder = false; - } else { - selector = new Selector(this, itemKey, { - key: selectorKey, - }); - this.selectors[selectorKey] = selector; - } - - return selector; - } - //========================================================================================================= // Get Group //========================================================================================================= /** * @public * Get Group by Key/Name - * @param groupKey - Name/Key of Group + * @param groupKey - Key/Name of Group * @param config - Config */ public getGroup( @@ -420,10 +370,7 @@ export class Collection { if (!group || (!config.notExisting && group.isPlaceholder)) return undefined; - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(group.observer); - + ComputedTracker.tracked(group.observer); return group; } @@ -432,29 +379,22 @@ export class Collection { //========================================================================================================= /** * @public - * Get Group by Key/Name or a Reference to it if it doesn't exist - * If Group doesn't exist, it returns a reference of the Group that will be filled with the real data later + * Get Group by Key/Name or a Reference to it if it doesn't exist yet * @param groupKey - Name/Key of Group */ - public getGroupWithReference( - groupKey: GroupKey | undefined - ): Group { - let group = groupKey ? this.groups[groupKey] : undefined; + public getGroupWithReference(groupKey: GroupKey): Group { + let group = this.getGroup(groupKey, { notExisting: true }); // Create dummy Group to hold reference if (!group) { - const dummyGroup = new Group(this, [], { + group = new Group(this, [], { key: groupKey, + isPlaceholder: true, }); - dummyGroup.isPlaceholder = true; - this.groups[groupKey || "unknown"] = dummyGroup; - return dummyGroup; + this.groups[groupKey] = group; } - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(group.observer); - + ComputedTracker.tracked(group.observer); return group; } @@ -468,22 +408,56 @@ export class Collection { */ public removeGroup(groupKey: GroupKey): this { if (!this.groups[groupKey]) { - console.warn( - `Agile: Group with the key/name '${groupKey}' doesn't exist!` - ); + Agile.logger.warn(`Group with the key/name '${groupKey}' doesn't exist!`); return this; } delete this.groups[groupKey]; return this; } + //========================================================================================================= + // Create Selector + //========================================================================================================= + /** + * @public + * Creates new Selector that represents an Item of the Collection + * @param selectorKey - Name/Key of Selector + * @param itemKey - Key of Item which the Selector represents + */ + public createSelector( + selectorKey: SelectorKey, + itemKey: ItemKey + ): Selector { + let selector = this.getSelector(selectorKey, { notExisting: true }); + + // Check if Selector already exists + if (selector) { + if (!selector.isPlaceholder) { + Agile.logger.warn( + `Selector with the name '${selectorKey}' already exists!` + ); + return selector; + } + selector.select(itemKey, { overwrite: true }); + return selector; + } + + // Create Selector + selector = new Selector(this, itemKey, { + key: selectorKey, + }); + this.selectors[selectorKey] = selector; + + return selector; + } + //========================================================================================================= // Get Selector //========================================================================================================= /** * @public * Get Selector by Key/Name - * @param selectorKey - Name/Key of Selector + * @param selectorKey - Key/Name of Selector * @param config - Config */ public getSelector( @@ -501,10 +475,7 @@ export class Collection { if (!selector || (!config.notExisting && selector.isPlaceholder)) return undefined; - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(selector.observer); - + ComputedTracker.tracked(selector.observer); return selector; } @@ -513,29 +484,24 @@ export class Collection { //========================================================================================================= /** * @public - * Get Selector by Key/Name or a Reference to it if it doesn't exist - * If Selector doesn't exist, it returns a reference of the Selector that will be filled with the real data later + * Get Selector by Key/Name or a Reference to it if it doesn't exist yet * @param selectorKey - Name/Key of Selector */ public getSelectorWithReference( - selectorKey: SelectorKey | undefined + selectorKey: SelectorKey ): Selector { - let selector = selectorKey ? this.selectors[selectorKey] : undefined; + let selector = this.getSelector(selectorKey, { notExisting: true }); - // Create dummy Group to hold reference + // Create dummy Selector to hold reference if (!selector) { - const dummySelector = new Selector(this, "unknown", { + selector = new Selector(this, "unknown", { key: selectorKey, + isPlaceholder: true, }); - dummySelector.isPlaceholder = true; - this.selectors[selectorKey || "unknown"] = dummySelector; - return dummySelector; + this.selectors[selectorKey] = selector; } - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(selector.observer); - + ComputedTracker.tracked(selector.observer); return selector; } @@ -549,31 +515,16 @@ export class Collection { */ public removeSelector(selectorKey: SelectorKey): this { if (!this.selectors[selectorKey]) { - console.warn( - `Agile: Selector with the key/name '${selectorKey}' doesn't exist!` + Agile.logger.warn( + `Selector with the key/name '${selectorKey}' doesn't exist!` ); return this; } + this.selectors[selectorKey]?.unselect(); // Unselects current selected Item delete this.selectors[selectorKey]; return this; } - //========================================================================================================= - // Remove - //========================================================================================================= - /** - * @public - * Remove Items from Group or from everywhere - * @param itemKeys - ItemKey/s that get removed - */ - public remove(itemKeys: ItemKey | Array) { - return { - fromGroups: (groups: Array | ItemKey) => - this.removeFromGroups(itemKeys, groups), - everywhere: () => this.removeItems(itemKeys), - }; - } - //========================================================================================================= // Get Item by Id //========================================================================================================= @@ -597,37 +548,34 @@ export class Collection { // Check if Item exists if (!item || (!config.notExisting && !item.exists)) return undefined; - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(item.observer); - + ComputedTracker.tracked(item.observer); return item; } /** * @public - * Get Item by Key/Name or a Reference to it if it doesn't exist - * If Item doesn't exist, it returns a reference of the Item that will be filled with the real data later - * @param itemKey - Name/Key of Item + * Get Item by Key/Name or a Reference to it if it doesn't exist yet + * @param itemKey - Key/Name of Item */ - public getItemWithReference(itemKey: ItemKey | undefined): Item { - let item = itemKey ? this.data[itemKey] : undefined; + public getItemWithReference(itemKey: ItemKey): Item { + let item = this.getItem(itemKey, { notExisting: true }); // Create dummy Item to hold reference if (!item) { - const dummyItem = new Item(this, { - [this.config.primaryKey]: itemKey, - dummy: true, - } as any); - dummyItem.isPlaceholder = true; - this.data[itemKey || "unknown"] = dummyItem; - return dummyItem; + item = new Item( + this, + { + [this.config.primaryKey]: itemKey, // Setting PrimaryKey of Item to passed itemKey + dummy: "item", + } as any, + { + isPlaceholder: true, + } + ); + this.data[itemKey] = item; } - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(item.observer); - + ComputedTracker.tracked(item.observer); return item; } @@ -638,9 +586,13 @@ export class Collection { * @public * Get Value of Item by Key/Name * @param itemKey - ItemKey of Item that holds the Value + * @param config - Config */ - public getItemValue(itemKey: ItemKey | undefined): DataType | undefined { - let item = this.getItem(itemKey); + public getItemValue( + itemKey: ItemKey | undefined, + config: GetItemConfigInterface = {} + ): DataType | undefined { + let item = this.getItem(itemKey, config); if (!item) return undefined; return item.value; } @@ -657,7 +609,7 @@ export class Collection { /** * @public * Stores Collection Value into Agile Storage permanently - * @param key - Storage Key (Note: not needed if Collection has key/name) + * @param key - Key/Name of created Persistent (Note: Key required if Collection has no set Key!) * @param config - Config */ public persist( @@ -673,7 +625,7 @@ export class Collection { if (isValidObject(keyOrConfig)) { _config = keyOrConfig as CollectionPersistentConfigInterface; - key = undefined; + key = this._key; } else { _config = config || {}; key = keyOrConfig as StorageKey; @@ -681,27 +633,49 @@ export class Collection { _config = defineConfig(_config, { instantiate: true, - storageKeys: undefined, + storageKeys: [], }); - // Update Persistent Key - if (this.persistent) { - this.persistent.storageKeys = config.storageKeys; - if (key) this.persistent.setKey(key); - return this; - } + if (this.persistent) + Agile.logger.warn( + `By persisting the Collection '${this._key}' twice you overwrite the old Persistent Instance!` + ); // Create persistent -> Persist Value - this.persistent = new CollectionPersistent(this, key, { + this.persistent = new CollectionPersistent(this, { instantiate: _config.instantiate, storageKeys: _config.storageKeys, + key: key, }); return this; } //========================================================================================================= - // Group Size + // On Load + //========================================================================================================= + /** + * @public + * Callback Function that gets called if the persisted Value gets loaded into the Collection for the first Time + * Note: Only useful for persisted Collections! + * @param callback - Callback Function + */ + public onLoad(callback: (success: boolean) => void): this { + if (this.persistent) { + this.persistent.onLoad = callback; + + // If Collection is already 'isPersisted' the loading was successful -> callback can be called + if (this.isPersisted) callback(true); + } else { + Agile.logger.error( + `Please make sure you persist the Collection '${this._key}' before using the 'onLoad' function!` + ); + } + return this; + } + + //========================================================================================================= + // Get Group Count //========================================================================================================= /** * @public @@ -714,7 +688,7 @@ export class Collection { } //========================================================================================================= - // Selector Size + // Get Selector Count //========================================================================================================= /** * @public @@ -734,24 +708,15 @@ export class Collection { * Resets this Collection */ public reset() { - // Remove Items from Storage - for (let key in this.data) { - const item = this.getItem(key); - item?.persistent?.removeValue(); - } - - // Reset Groups - for (let key in this.groups) this.getGroup(key)?.reset(); - // Reset Data this.data = {}; this.size = 0; - // Reselect Items - for (let key in this.selectors) { - const selector = this.getSelector(key); - selector?.select(selector?.itemKey, { force: true }); - } + // Reset Groups + for (let key in this.groups) this.getGroup(key)?.reset(); + + // Reset Selectors + for (let key in this.selectors) this.getSelector(key)?.reset(); } //========================================================================================================= @@ -774,10 +739,7 @@ export class Collection { // Add ItemKeys to Groups _groupKeys.forEach((groupKey) => { - const group = this.getGroup(groupKey); - _itemKeys.forEach((itemKey) => { - group?.add(itemKey, config); - }); + this.getGroup(groupKey)?.add(_itemKeys, config); }); } @@ -786,52 +748,96 @@ export class Collection { //========================================================================================================= /** * @internal - * Updates ItemKey of Item + * Updates Key/Name of Item in all Instances (Group, Selector, ..) * @param oldItemKey - Old ItemKey * @param newItemKey - New ItemKey * @param config - Config */ - private updateItemKey( + public updateItemKey( oldItemKey: ItemKey, newItemKey: ItemKey, - config?: UpdateItemKeyConfigInterface - ): void { - const item = this.getItem(oldItemKey); + config: UpdateItemKeyConfigInterface = {} + ): boolean { + const item = this.getItem(oldItemKey, { notExisting: true }); config = defineConfig(config, { background: false, }); - if (!item || oldItemKey === newItemKey) return; + if (!item || oldItemKey === newItemKey) return false; // Remove Item from old ItemKey and add Item to new ItemKey delete this.data[oldItemKey]; this.data[newItemKey] = item; // Update Key/Name of Item - item.key = newItemKey; + item.setKey(newItemKey); - // Update persist Key of Item (Doesn't get changed by setting new item key because PersistKey is not ItemKey) - item.persist(CollectionPersistent.getItemStorageKey(newItemKey, this.key)); + // Update persist Key of Item (Doesn't get updated by updating key of Item because PersistKey is special formatted) + item.persistent?.setKey( + CollectionPersistent.getItemStorageKey(newItemKey, this._key) + ); + + // Update ItemKey in Groups + for (let groupKey in this.groups) { + const group = this.getGroup(groupKey, { notExisting: true }); + if (!group || !group.has(oldItemKey)) continue; + group.replace(oldItemKey, newItemKey, { background: config?.background }); + } - // Update Groups - for (let groupName in this.groups) { - const group = this.getGroup(groupName); - if (!group || group.isPlaceholder || !group.has(oldItemKey)) continue; + // Update ItemKey in Selectors + for (let selectorKey in this.selectors) { + const selector = this.getSelector(selectorKey, { notExisting: true }); + if (!selector) continue; - // Replace old ItemKey with new ItemKey - const newGroupValue = copy(group.value); - newGroupValue.splice(newGroupValue.indexOf(oldItemKey), 1, newItemKey); - group.set(newGroupValue, { background: config?.background }); + // Reselect Item in existing Selector which has selected the newItemKey + if (selector.hasSelected(newItemKey)) { + selector.select(newItemKey, { + force: true, // Because ItemKeys are the same + background: config?.background, + }); + } + + // Select newItemKey in existing Selector which has selected the oldItemKey + if (selector.hasSelected(oldItemKey)) + selector.select(newItemKey, { + background: config?.background, + }); } - // Update Selectors - for (let selectorName in this.selectors) { - const selector = this.getSelector(selectorName); - if (!selector || selector.itemKey !== oldItemKey) continue; + return true; + } - // Replace old selected ItemKey with new ItemKey - selector.select(newItemKey, { background: config?.background }); + //========================================================================================================= + // Get GroupKeys That Have ItemKey + //========================================================================================================= + /** + * @public + * Gets GroupKeys that contain the passed ItemKey + * @param itemKey - ItemKey + */ + public getGroupKeysThatHaveItemKey(itemKey: ItemKey): Array { + const groupKeys: Array = []; + for (let groupKey in this.groups) { + const group = this.getGroup(groupKey, { notExisting: true }); + if (group?.has(itemKey)) groupKeys.push(groupKey); } + return groupKeys; + } + + //========================================================================================================= + // Remove + //========================================================================================================= + /** + * @public + * Remove Items from Collection + * @param itemKeys - ItemKey/s that get removed + */ + public remove(itemKeys: ItemKey | Array) { + return { + fromGroups: (groups: Array | ItemKey) => + this.removeFromGroups(itemKeys, groups), + everywhere: () => this.removeItems(itemKeys), + }; } //========================================================================================================= @@ -855,14 +861,17 @@ export class Collection { // Remove ItemKey from Groups _groupKeys.forEach((groupKey) => { - const group = this.getGroup(groupKey); - if (!group) return; + const group = this.getGroup(groupKey, { notExisting: true }); + if (!group || !group.has(itemKey)) return; group.remove(itemKey); removedFromGroupsCount++; }); - // If Item got removed from every Groups in Collection, remove it completely - if (removedFromGroupsCount >= this.getGroupCount()) + // If Item got removed from every Groups the Item was in, remove it completely + if ( + removedFromGroupsCount >= + this.getGroupKeysThatHaveItemKey(itemKey).length + ) this.removeItems(itemKey); }); } @@ -872,34 +881,35 @@ export class Collection { //========================================================================================================= /** * @public - * Removes Item/s from Group/s - * @param itemKeys - ItemKey/s of Item/s that get removed from Collection + * Removes Item completely from Collection + * @param itemKeys - ItemKey/s of Item/s */ public removeItems(itemKeys: ItemKey | Array): void { const _itemKeys = normalizeArray(itemKeys); _itemKeys.forEach((itemKey) => { - const item = this.getItem(itemKey); + const item = this.getItem(itemKey, { notExisting: true }); if (!item) return; // Remove Item from Groups for (let groupKey in this.groups) { - const group = this.getGroup(groupKey); - if (group && group.has(itemKey)) group.remove(itemKey); - } - - // Remove Selectors that represents this Item - for (let selectorKey in this.selectors) { - const selector = this.getSelector(selectorKey); - if (selector?.itemKey === itemKey) this.removeSelector(selectorKey); + const group = this.getGroup(groupKey, { notExisting: true }); + if (group?.has(itemKey)) group?.remove(itemKey); } // Remove Item from Storage - item.persistent?.removeValue(); + item.persistent?.removePersistedValue(); // Remove Item from Collection delete this.data[itemKey]; + // Reselect Item in Selectors (to create new dummyItem that holds reference) + for (let selectorKey in this.selectors) { + const selector = this.getSelector(selectorKey, { notExisting: true }); + if (selector?.hasSelected(itemKey)) + selector?.select(itemKey, { force: true }); + } + this.size--; }); } @@ -909,15 +919,12 @@ export class Collection { //========================================================================================================= /** * @internal - * Creates/Updates Item from provided Data and adds it to the Collection - * @param data - Data from which the Item gets created/updated + * Updates existing or creates Item from provided Data + * @param data - Data * @param config - Config */ - public setData( - data: DataType, - config: { patch?: boolean; background?: boolean } = {} - ): boolean { - const _data = data as any; // Transformed Data to any because of unknown Object (DataType) + public setData(data: DataType, config: SetDataConfigInterface = {}): boolean { + const _data = copy(data as any); // Transformed Data to any because of unknown Object (DataType) const primaryKey = this.config.primaryKey; config = defineConfig(config, { patch: false, @@ -925,32 +932,36 @@ export class Collection { }); if (!isValidObject(_data)) { - console.error("Agile: Collections items has to be an object!"); + Agile.logger.error( + `Item Data of Collection '${this._key}' has to be an valid Object!` + ); return false; } if (!_data.hasOwnProperty(primaryKey)) { - console.error( - `Agile: Collection Item needs a primary Key property called '${this.config.primaryKey}'!` + Agile.logger.error( + `Collection '${this._key}' Item Data has to contain a primaryKey property called '${this.config.primaryKey}'!` ); return false; } const itemKey = _data[primaryKey]; - let item: Item | undefined = this.data[itemKey]; + let item = this.getItem(itemKey, { notExisting: true }); + const wasPlaceholder = item?.isPlaceholder || false; + const createItem = !item; // Create or update Item - if (item && config.patch) - item.patch(_data, { background: config.background }); - if (item && !config.patch) - item.set(_data, { background: config.background }); - if (!item) { + if (!createItem && config.patch) + item?.patch(_data, { background: config.background }); + if (!createItem && !config.patch) + item?.set(_data, { background: config.background }); + if (createItem) { item = new Item(this, _data); - this.size++; + this.data[itemKey] = item; } - // Set new Item at itemKey - this.data[itemKey] = item; + // Increase size of Collection + if (createItem || wasPlaceholder) this.size++; return true; } @@ -976,9 +987,9 @@ export class Collection { // Rebuild Groups that include ItemKey for (let groupKey in this.groups) { const group = this.getGroup(groupKey); - if (group && group.has(itemKey)) { + if (group?.has(itemKey)) { // group.rebuild(); Not necessary because a sideEffect of the Group is to rebuild it self - group.ingest({ + group?.ingest({ background: config?.background, force: true, // because Group value doesn't change only the output changes sideEffects: config?.sideEffects, @@ -991,7 +1002,7 @@ export class Collection { export type DefaultItem = { [key: string]: any }; export type CollectionKey = string | number; -export type ItemKey = string | number; // Key Interface of Item in Collection +export type ItemKey = string | number; /** * @param key - Key/Name of Collection @@ -1022,12 +1033,14 @@ export interface CollectionConfigInterface { * @param method - Way of adding Item to Collection (push, unshift) * @param forEachItem - Gets called for each Item that got collected * @param background - If collecting an Item happens in the background (-> not causing any rerender) + * @param select - If collected Items get selected with a Selector */ export interface CollectConfigInterface { patch?: boolean; method?: "push" | "unshift"; forEachItem?: (data: DataType, key: ItemKey, index: number) => void; background?: boolean; + select?: boolean; } /** @@ -1078,6 +1091,24 @@ export interface GetSelectorConfigInterface { notExisting?: boolean; } +/** + * @param instantiate - If Persistent gets instantiated + * @param storageKeys - Key/Name of Storages which gets used to persist the Collection Value (NOTE: If not passed the default Storage will be used) + */ +export interface CollectionPersistentConfigInterface { + instantiate?: boolean; + storageKeys?: StorageKey[]; +} + +/** + * @param patch - If Data gets patched into existing Item + * @param background - If assigning Data happens in background + */ +export interface SetDataConfigInterface { + patch?: boolean; + background?: boolean; +} + export type CollectionConfig = | CreateCollectionConfigInterface | ((collection: Collection) => CreateCollectionConfigInterface); diff --git a/packages/core/src/collection/item.ts b/packages/core/src/collection/item.ts index be945535..94f7d0de 100644 --- a/packages/core/src/collection/item.ts +++ b/packages/core/src/collection/item.ts @@ -1,46 +1,62 @@ import { State, Collection, DefaultItem, StateKey } from "../internal"; export class Item extends State { - private collection: () => Collection; + static updateGroupSideEffectKey = "rebuildGroup"; + public isSelected = false; // If Item is selected by a Selector + public collection: () => Collection; /** * @public * Item of Collection * @param collection - Collection to which the Item belongs * @param data - Data that the Item holds + * @param config - Config */ - constructor(collection: Collection, data: DataType) { - super(collection.agileInstance(), data); + constructor( + collection: Collection, + data: DataType, + config: ItemConfigInterface = {} + ) { + super(collection.agileInstance(), data, { + isPlaceholder: config.isPlaceholder, + key: data[collection.config.primaryKey], // Set Key/Name of Item to primaryKey of Data + }); this.collection = () => collection; - // Setting primaryKey of Data to Key/Name of Item - this.key = data[collection.config.primaryKey]; + // Reassign Key to assign sideEffects + this.setKey(data[collection.config.primaryKey]); } + //========================================================================================================= + // Set Key + //========================================================================================================= /** - * @public - * Set Key/Name of Item + * @internal + * Updates Key/Name of State + * @param value - New Key/Name of State */ - public set key(value: StateKey | undefined) { - // Note can't use 'super.key' because of 'https://github.com/Microsoft/TypeScript/issues/338' - this.setKey(value); - if (!value) return; - - // Update rebuildGroupThatIncludePrimaryKey SideEffect - this.removeSideEffect("rebuildGroup"); - this.addSideEffect("rebuildGroup", (properties: any) => - this.collection().rebuildGroupsThatIncludeItemKey(value, properties) + public setKey(value: StateKey | undefined): this { + super.setKey(value); + if (!value) return this; + + // Remove old rebuildGroupsThatIncludeItemKey sideEffect + this.removeSideEffect(Item.updateGroupSideEffectKey); + + // Add rebuildGroupsThatIncludeItemKey to sideEffects to rebuild Groups that include this Item if it changes + this.addSideEffect(Item.updateGroupSideEffectKey, (config) => + this.collection().rebuildGroupsThatIncludeItemKey(value, config) ); - } - /** - * @public - * Get Key/Name of Item - */ - public get key(): StateKey | undefined { - // Note can't use 'super.key' because of 'https://github.com/Microsoft/TypeScript/issues/338' - // Can't remove this getter function.. because the setter function is set in this class -> Error if not setter and getter function set + // Initial Rebuild + this.collection().rebuildGroupsThatIncludeItemKey(value); - return this._key; + return this; } } + +/** + * @param isPlaceholder - If Item is initially a Placeholder + */ +export interface ItemConfigInterface { + isPlaceholder?: boolean; +} diff --git a/packages/core/src/collection/selector.ts b/packages/core/src/collection/selector.ts index 40d1e5e8..8e8d4948 100644 --- a/packages/core/src/collection/selector.ts +++ b/packages/core/src/collection/selector.ts @@ -1,17 +1,19 @@ import { Agile, Collection, - copy, DefaultItem, defineConfig, Item, ItemKey, State, + StateRuntimeJobConfigInterface, } from "../internal"; export class Selector extends State< DataType | undefined > { + static dummyItemKey = "unknown"; + static rebuildSelectorSideEffectKey = "rebuildSelector"; public collection: () => Collection; public item: Item | undefined; public _itemKey: ItemKey; // Key of Item the Selector represents @@ -26,16 +28,21 @@ export class Selector extends State< constructor( collection: Collection, itemKey: ItemKey, - config?: SelectorConfigInterface + config: SelectorConfigInterface = {} ) { - super(collection.agileInstance(), collection.getItemValue(itemKey)); + super(collection.agileInstance(), undefined, config); + config = defineConfig(config, { + isPlaceholder: false, + }); + this.collection = () => collection; this.item = undefined; - this._itemKey = itemKey; - this.key = config?.key; + this._itemKey = Selector.dummyItemKey; + this._key = config?.key; + this.isPlaceholder = true; // Initial Select - this.select(itemKey); + if (!config.isPlaceholder) this.select(itemKey, { overwrite: true }); } /** @@ -54,40 +61,45 @@ export class Selector extends State< return this._itemKey; } + //========================================================================================================= + // Select + //========================================================================================================= /** * @public - * Select new ItemKey that the Selector will represents + * Select new ItemKey * @param itemKey - New ItemKey * @param config - Config */ - public select(itemKey: ItemKey, config: SelectConfigInterface = {}): this { - const oldItem = this.item; + public select( + itemKey: ItemKey, + config: StateRuntimeJobConfigInterface = {} + ): this { + const oldItem = this.collection().getItem(this._itemKey, { + notExisting: true, + }); // Because this.item might be outdated let newItem = this.collection().getItemWithReference(itemKey); - const rebuildSelectorSideEffectKey = "rebuildSelector"; config = defineConfig(config, { background: false, sideEffects: true, force: false, + overwrite: oldItem?.isPlaceholder || false, + storage: true, }); - if (oldItem?.key === itemKey && !config.force) { - Agile.logger.warn( - `Agile: Selector has already a selected key '${itemKey}'!` - ); + if (this.hasSelected(itemKey) && !config.force) { + Agile.logger.warn(`Selector has already selected '${itemKey}'!`); return this; } - // Remove old Item from Collection if it is an Placeholder - if (oldItem?.isPlaceholder) delete this.collection().data[this.itemKey]; - - // Remove Selector sideEffect from old Item - oldItem?.removeSideEffect(rebuildSelectorSideEffectKey); + // Unselect old Item + this.unselect({ background: true }); this._itemKey = itemKey; this.item = newItem; + newItem.isSelected = true; - // Add Selector sideEffect to Item - newItem.addSideEffect(rebuildSelectorSideEffectKey, () => + // Add SideEffect to newItem, that rebuild this Selector depending on the current Item Value + newItem.addSideEffect(Selector.rebuildSelectorSideEffectKey, (config) => this.rebuildSelector(config) ); @@ -98,30 +110,66 @@ export class Selector extends State< } //========================================================================================================= - // RebuildSelector + // Unselect //========================================================================================================= /** * @public - * Rebuilds Selector + * Unselects current selected Item * @param config - Config */ - public rebuildSelector(config: SelectConfigInterface = {}) { - config = defineConfig(config, { - background: false, - sideEffects: true, + public unselect(config: StateRuntimeJobConfigInterface = {}): this { + // Because this.item might be outdated + const item = this.collection().getItem(this._itemKey, { + notExisting: true, }); + // Unselect Item + if (item) { + item.isSelected = false; + item.removeSideEffect(Selector.rebuildSelectorSideEffectKey); + if (item.isPlaceholder) delete this.collection().data[this._itemKey]; + } + + // Reset and rebuild Selector + this.item = undefined; + this._itemKey = Selector.dummyItemKey; + this.rebuildSelector(config); + + this.isPlaceholder = true; + + return this; + } + + //========================================================================================================= + // Has Selected + //========================================================================================================= + /** + * Checks if Selector has selected passed ItemKey + * @param itemKey + */ + public hasSelected(itemKey: ItemKey): boolean { + const isSelected = this._itemKey === itemKey; + if (!this.item) return isSelected; + return isSelected && this.item.isSelected; + } + + //========================================================================================================= + // Rebuild Selector + //========================================================================================================= + /** + * @public + * Rebuilds Selector + * @param config - Config + */ + public rebuildSelector(config: StateRuntimeJobConfigInterface = {}) { // Set Selector Value to undefined if Item doesn't exist if (!this.item || this.item.isPlaceholder) { - this._value = undefined; + this.set(undefined, config); return; } - // Assign ItemValue to Selector - this.nextStateValue = copy(this.item?.value); - - // Ingest nextStateValue into Runtime - this.ingest(config); + // Set Selector Value to updated Item Value + this.set(this.item._value, config); } } @@ -129,18 +177,9 @@ export type SelectorKey = string | number; /** * @param key - Key/Name of Selector + * @param isPlaceholder - If Selector is initially a Placeholder */ export interface SelectorConfigInterface { key?: SelectorKey; -} - -/** - * @param background - If selecting a new Item happens in the background (-> not causing any rerender) - * @param sideEffects - If Side Effects of Selector get executed - * @param force - Force to select ItemKey - */ -export interface SelectConfigInterface { - background?: boolean; - sideEffects?: boolean; - force?: boolean; + isPlaceholder?: boolean; } diff --git a/packages/core/src/computed/computed.tracker.ts b/packages/core/src/computed/computed.tracker.ts new file mode 100644 index 00000000..7f2bd126 --- /dev/null +++ b/packages/core/src/computed/computed.tracker.ts @@ -0,0 +1,46 @@ +import { Observer } from "../runtime/observer"; + +export class ComputedTracker { + static isTracking = false; + static trackedObservers: Set = new Set(); + + //========================================================================================================= + // Track + //========================================================================================================= + /** + * @internal + * Starts tracking Observers + */ + static track(): void { + this.isTracking = true; + } + + //========================================================================================================= + // Tracked + //========================================================================================================= + /** + * @internal + * Adds passed Observer to tracked Observers, if ComputedTracker is currently tracking + * @param observer - Observer + */ + static tracked(observer: Observer) { + if (this.isTracking) this.trackedObservers.add(observer); + } + + //========================================================================================================= + // Get Tracked Observers + //========================================================================================================= + /** + * @internal + * Returns tracked Observers and stops tracking anymore Observers + */ + static getTrackedObservers(): Array { + const trackedObservers = Array.from(this.trackedObservers); + + // Reset tracking + this.isTracking = false; + this.trackedObservers = new Set(); + + return trackedObservers; + } +} diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 02b19649..2f5ddf1d 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -5,6 +5,9 @@ import { Observer, StorageKey, StatePersistentConfigInterface, + Event, + StateConfigInterface, + ComputedTracker, } from "../internal"; export class Computed extends State< @@ -13,65 +16,48 @@ export class Computed extends State< public agileInstance: () => Agile; public computeFunction: () => ComputedValueType; - public deps: Array = []; // All Dependencies of Computed - public hardCodedDeps: Array = []; + public deps: Array = []; // All Dependencies of Computed (hardCoded and autoDetected) + public hardCodedDeps: Array = []; // HardCoded Dependencies of Computed /** * @public * Computed - Function that recomputes its value if a dependency changes * @param agileInstance - An instance of Agile * @param computeFunction - Function for computing value - * @param deps - Hard coded dependencies of Computed Function + * @param config - Config */ constructor( agileInstance: Agile, computeFunction: () => ComputedValueType, - deps: Array = [] + config: ComputedConfigInterface = {} ) { - super(agileInstance, computeFunction()); + super(agileInstance, computeFunction(), { + key: config.key, + deps: config.deps, + }); + config = defineConfig(config, { + computedDeps: [], + }); this.agileInstance = () => agileInstance; this.computeFunction = computeFunction; - this.hardCodedDeps = deps - .map((dep) => dep["observer"] || undefined) - .filter((dep) => dep !== undefined); + + // Format hardCodedDeps + this.hardCodedDeps = this.formatDeps(config.computedDeps as any); + this.deps = this.hardCodedDeps; // Recompute for setting initial value and adding missing dependencies this.recompute(); } - /** - * @public - * Set Value of Computed - */ - public set value(value: ComputedValueType) { - Agile.logger.error("You can't mutate Computed value!"); - } - - /** - * @public - * Get Value of Computed - */ - public get value(): ComputedValueType { - // Note can't use 'super.value' because of 'https://github.com/Microsoft/TypeScript/issues/338' - // Can't remove this getter function.. since the setter function is set in this class -> Error if not setter and getter function set - - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(this.observer); - - return this._value; - } - //========================================================================================================= // Recompute //========================================================================================================= /** * @public - * Recomputes Function Value - * -> Calls ComputeFunction and updates Dependencies of it + * Recomputes Value of Computed * @param config - Config */ - public recompute(config?: RecomputeConfigInterface) { + public recompute(config: RecomputeConfigInterface = {}) { config = defineConfig(config, { background: false, sideEffects: true, @@ -92,15 +78,28 @@ export class Computed extends State< public updateComputeFunction( computeFunction: () => ComputedValueType, deps: Array = [], - config?: RecomputeConfigInterface + config: UpdateComputeFunctionInterface = {} ) { + config = defineConfig(config, { + background: false, + sideEffects: true, + overwriteDeps: true, + }); + + // Update deps + const newDeps = this.formatDeps(deps as any); + if (config.overwriteDeps) this.hardCodedDeps = newDeps; + else this.hardCodedDeps = this.hardCodedDeps.concat(newDeps); + this.deps = this.hardCodedDeps; + + // Update computeFunction this.computeFunction = computeFunction; - this.hardCodedDeps = deps - .map((dep) => dep["observer"] || undefined) - .filter((dep) => dep !== undefined); // Recompute for setting initial Computed Function Value and adding missing Dependencies - this.recompute(config); + this.recompute({ + background: config.background, + sideEffects: config.sideEffects, + }); } //========================================================================================================= @@ -111,21 +110,17 @@ export class Computed extends State< * Computes Value and adds missing Dependencies to Computed */ public computeValue(): ComputedValueType { - this.agileInstance().runtime.trackObservers = true; + // Auto track Observers the computeFunction might depend on + ComputedTracker.track(); const computedValue = this.computeFunction(); - - // Get tracked Observers and disable Tracking Observers - let foundDeps = Array.from( - this.agileInstance().runtime.getTrackedObservers() - ); + let foundDeps = ComputedTracker.getTrackedObservers(); // Handle foundDeps and hardCodedDeps const newDeps: Array = []; this.hardCodedDeps.concat(foundDeps).forEach((observer) => { - if (!observer) return; newDeps.push(observer); - // Make this Observer depending on Observer -> If value of Observer changes it will ingest this Observer into the Runtime + // Make this Observer depending on foundDep Observer observer.depend(this.observer); }); @@ -134,11 +129,36 @@ export class Computed extends State< } //========================================================================================================= - // Overwriting some functions which can't be used in Computed + // Format Deps + //========================================================================================================= + /** + * @internal + * Gets Observer out of passed Instances + * @param instances - Instances that hold an Observer + */ + public formatDeps(instances: Array): Array { + const finalInstances: Array = []; + for (let instance of instances) { + if (instance instanceof Observer) { + finalInstances.push(instance); + continue; + } + if ( + instance !== undefined && + instance["observer"] !== undefined && + instance["observer"] instanceof Observer + ) + finalInstances.push(instance["observer"]); + } + return finalInstances; + } + + //========================================================================================================= + // Overwriting some functions which aren't allowed to use in Computed //========================================================================================================= public patch() { - Agile.logger.error("You can't use patch method on Computed Function!"); + Agile.logger.error("You can't use patch method on ComputedState!"); return this; } @@ -146,16 +166,23 @@ export class Computed extends State< keyOrConfig: StorageKey | StatePersistentConfigInterface = {}, config: StatePersistentConfigInterface = {} ): this { - Agile.logger.error("You can't use persist method on Computed Function!"); + Agile.logger.error("You can't use persist method on ComputedState!"); return this; } public invert(): this { - Agile.logger.error("You can't use invert method on Computed Function!"); + Agile.logger.error("You can't use invert method on ComputedState!"); return this; } } +/** + * @param computedDeps - Hard coded dependencies of Computed Function + */ +export interface ComputedConfigInterface extends StateConfigInterface { + computedDeps?: Array; +} + /** * @param background - If recomputing value happens in the background (-> not causing any rerender) * @param sideEffects - If Side Effects of Computed get executed @@ -164,3 +191,11 @@ export interface RecomputeConfigInterface { background?: boolean; sideEffects?: boolean; } + +/** + * @param overwriteDeps - If old hardCoded deps get overwritten + */ +export interface UpdateComputeFunctionInterface + extends RecomputeConfigInterface { + overwriteDeps?: boolean; +} diff --git a/packages/core/src/event/event.job.ts b/packages/core/src/event/event.job.ts index 964d27aa..f192cadd 100644 --- a/packages/core/src/event/event.job.ts +++ b/packages/core/src/event/event.job.ts @@ -1,14 +1,17 @@ export class EventJob { public payload: PayloadType; public creationTimestamp: number; + public keys?: string[]; /** * @public * Event Job - Holds Payload and gets executed/performed by the Event * @param payload - Payload that is represented by this Job + * @param keys - Keys of EventCallbacks that get executed with the passed payload */ - constructor(payload: PayloadType) { + constructor(payload: PayloadType, keys?: string[]) { this.payload = payload; + this.keys = keys; this.creationTimestamp = new Date().getTime(); } } diff --git a/packages/core/src/event/event.observer.ts b/packages/core/src/event/event.observer.ts index e3f32c11..0684d2e5 100644 --- a/packages/core/src/event/event.observer.ts +++ b/packages/core/src/event/event.observer.ts @@ -1,4 +1,14 @@ -import { Agile, Observer, Job, ObserverKey, Event } from "../internal"; +import { + Observer, + RuntimeJob, + ObserverKey, + Event, + SubscriptionContainer, + IngestConfigInterface, + RuntimeJobConfigInterface, + defineConfig, + RuntimeJobKey, +} from "../internal"; export class EventObserver extends Observer { public event: () => Event; @@ -6,18 +16,18 @@ export class EventObserver extends Observer { /** * @internal * Event Observer - Handles Event dependencies and ingests Event triggers into the Runtime - * @param agileInstance - An instance of Agile * @param event - Event - * @param deps - Initial Dependencies of the Event - * @param key - Key/Name of Event Observer + * @param config - Config */ constructor( - agileInstance: Agile, event: Event, - deps?: Array, - key?: ObserverKey + config: CreateEventObserverConfigInterface = {} ) { - super(agileInstance, deps, key); + super(event.agileInstance(), { + deps: config.deps, + key: config.key, + subs: config.subs, + }); this.event = () => event; } @@ -27,9 +37,27 @@ export class EventObserver extends Observer { /** * @internal * Ingests Event into Runtime and causes Rerender on Components that got subscribed by the Event (Observer) + * @param config - Config */ - public trigger(): void { - this.agileInstance().runtime.ingest(this, {}); + public trigger(config: EventIngestConfigInterface = {}): void { + config = defineConfig(config, { + perform: true, + background: false, + sideEffects: true, + force: false, + }); + + // Create Job + const job = new RuntimeJob(this, { + force: config.force, + sideEffects: config.sideEffects, + background: config.background, + key: config.key || this._key, + }); + + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -40,7 +68,27 @@ export class EventObserver extends Observer { * Performs Job from Runtime * @param job - Job that gets performed */ - public perform(job: Job) { + public perform(job: RuntimeJob) { // Noting to perform } } + +/** + * @param deps - Initial Dependencies of Event Observer + * @param subs - Initial Subscriptions of Event Observer + * @param key - Key/Name of Event Observer + */ +export interface CreateEventObserverConfigInterface { + deps?: Array; + subs?: Array; + key?: ObserverKey; +} + +/** + * @param key - Key/Name of Job that gets created + */ +export interface EventIngestConfigInterface + extends RuntimeJobConfigInterface, + IngestConfigInterface { + key?: RuntimeJobKey; +} diff --git a/packages/core/src/event/index.ts b/packages/core/src/event/index.ts index e537dcc7..dbe8f315 100644 --- a/packages/core/src/event/index.ts +++ b/packages/core/src/event/index.ts @@ -4,6 +4,7 @@ import { EventJob, generateId, isFunction, + Observer, } from "../internal"; import { EventObserver } from "./event.observer"; @@ -14,13 +15,14 @@ export class Event { private initialConfig: CreateEventConfigInterface; public _key?: EventKey; - public uses: number = 0; + public uses = 0; public callbacks: { [key: string]: EventCallbackFunction } = {}; // All 'subscribed' callback function - private currentTimeout: any; // Timeout that is active right now (delayed Event) - private queue: Array = []; // Queue of delayed Events - public enabled: boolean = true; + public enabled = true; public observer: EventObserver; + public currentTimeout: any; // Timeout that is active right now (delayed Event) + public queue: Array = []; // Queue of delayed Events + // @ts-ignore public payload: PayloadType; // Holds type of Payload so that it can be read external (never defined) @@ -37,14 +39,20 @@ export class Event { rerender: false, maxUses: undefined, delay: undefined, + overlap: false, + deps: [], }); this._key = config.key; - this.observer = new EventObserver(agileInstance, this, [], config.key); + this.observer = new EventObserver(this, { + key: config.key, + deps: config.deps, + }); this.enabled = config.enabled as any; this.config = { rerender: config.rerender as any, delay: config.delay, maxUses: config.maxUses, + overlap: config.overlap, }; this.initialConfig = config; } @@ -54,8 +62,7 @@ export class Event { * Set Key/Name of Event */ public set key(value: EventKey | undefined) { - this._key = value; - this.observer.key = value; + this.setKey(value); } /** @@ -66,6 +73,20 @@ export class Event { return this._key; } + //========================================================================================================= + // Set Key + //========================================================================================================= + /** + * @internal + * Set Key/Name of Event + * @param value - New Key/Name of Event + */ + public setKey(value: EventKey | undefined): this { + this._key = value; + this.observer._key = value; + return this; + } + //========================================================================================================= // On // https://stackoverflow.com/questions/12688275/is-there-a-way-to-do-method-overloading-in-typescript/12689054#12689054 @@ -102,14 +123,16 @@ export class Event { // Check if Callback is a Function if (!isFunction(_callback)) { - Agile.logger.error("A Event Callback Function has to be an function!"); + Agile.logger.error( + "A Event Callback Function has to be typeof Function!" + ); return this; } // Check if Callback Function already exists if (this.callbacks[key]) { Agile.logger.error( - `Event Callback Function with the key/name ${key} already exists!` + `Event Callback Function with the key/name '${key}' already exists!` ); return this; } @@ -123,7 +146,7 @@ export class Event { //========================================================================================================= /** * @public - * Triggers all or specific Events + * Triggers Events * @param payload - Payload that gets passed into the Callback Functions * @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) */ @@ -169,7 +192,10 @@ export class Event { public reset() { this.enabled = this.initialConfig.enabled as any; this.uses = 0; - if (this.currentTimeout) clearTimeout(this.currentTimeout); + if (this.currentTimeout) { + clearTimeout(this.currentTimeout); + this.currentTimeout = undefined; + } return this; } @@ -191,11 +217,11 @@ export class Event { //========================================================================================================= /** * @internal - * Triggers Event + * Triggers normal Event * @param payload - Payload that gets passed into the Callback Functions * @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) */ - private normalTrigger(payload: PayloadType, keys?: string[]) { + public normalTrigger(payload: PayloadType, keys?: string[]) { // Call wished Callback Functions if (!keys) { for (let key in this.callbacks) this.callbacks[key](payload); @@ -217,31 +243,41 @@ export class Event { //========================================================================================================= /** * @internal - * Triggers Event with some delay + * Triggers async Event (Events with a delay) * @param payload - Payload that gets passed into the Callback Functions * @param delay - Delay until Events get triggered * @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) */ - private delayedTrigger(payload: PayloadType, delay: number, keys?: string[]) { - // Check if a Timeout is currently active if so add payload to queue + public delayedTrigger(payload: PayloadType, delay: number, keys?: string[]) { + const eventJob = new EventJob(payload, keys); + + // Execute Event no matter if another event is currently active + if (this.config.overlap) { + setTimeout(() => { + this.normalTrigger(eventJob.payload, eventJob.keys); + }, delay); + return; + } + + // Check if a Event(Timeout) is currently active if so add EventJob to queue if (this.currentTimeout !== undefined) { - if (payload) this.queue.push(new EventJob(payload)); + if (payload) this.queue.push(eventJob); return; } - // Triggers Callback Functions and calls itself again if queue isn't empty - const looper = (payload: PayloadType) => { + // Executes EventJob and calls itself again if queue isn't empty to execute the next EventJob + const looper = (eventJob: EventJob) => { this.currentTimeout = setTimeout(() => { this.currentTimeout = undefined; - this.normalTrigger(payload, keys); + this.normalTrigger(eventJob.payload, eventJob.keys); if (this.queue.length > 0) { const nextEventJob = this.queue.shift(); - if (nextEventJob) looper(nextEventJob.payload); + if (nextEventJob) looper(nextEventJob); } }, delay); }; - looper(payload); + looper(eventJob); return; } } @@ -257,23 +293,29 @@ export type EventCallbackFunction = ( * @param enabled - If Event can be triggered * @param maxUses - How often the Event can be used/triggered * @param delay - Delayed call of Event Callback Functions in milliseconds + * @param overlap - If Events can overlap * @param rerender - If triggering an Event should cause a rerender + * @param deps - Initial deps of State */ export interface CreateEventConfigInterface { key?: EventKey; enabled?: boolean; maxUses?: number; delay?: number; + overlap?: boolean; rerender?: boolean; + deps?: Array; } /** * @param maxUses - How often the Event can be used/triggered * @param delay - Delayed call of Event Callback Functions in seconds + * @param overlap - If Events can overlap * @param rerender - If triggering an Event should cause a rerender */ export interface EventConfigInterface { maxUses?: number; delay?: number; + overlap?: boolean; rerender: boolean; } diff --git a/packages/core/src/integrations/index.ts b/packages/core/src/integrations/index.ts index 5a5fbdd9..d2403bbb 100644 --- a/packages/core/src/integrations/index.ts +++ b/packages/core/src/integrations/index.ts @@ -24,26 +24,32 @@ export class Integrations { //========================================================================================================= /** * @internal - * Integrates Framework (Integration) into Agile - * @param integration - Integration that gets registered/integrated + * Integrates Framework(Integration) into Agile + * @param integration - Integration/Framework that gets integrated */ - public async integrate(integration: Integration) { - // Check if integration is valid - if (!integration.config.name) { - console.error("Agile: Failed to integrate framework!"); - return; + public async integrate(integration: Integration): Promise { + // Check if Integration is valid + if (!integration._key) { + Agile.logger.error( + "Failed to integrate framework! Invalid Integration!", + integration._key + ); + return false; } - // Integrate Integration/Framework - this.integrations.add(integration); - if (integration.config.bind) - integration.ready = await integration.config.bind(this.agileInstance()); + // Bind Framework to Agile + if (integration.methods.bind) + integration.ready = await integration.methods.bind(this.agileInstance()); else integration.ready = true; + // Integrate Framework + this.integrations.add(integration); + integration.integrated = true; + // Logging - Agile.logger.info( - `Agile: Successfully integrated '${integration.config.name}'` - ); + Agile.logger.info(`Successfully integrated '${integration._key}'`); + + return true; } //========================================================================================================= @@ -51,23 +57,19 @@ export class Integrations { //========================================================================================================= /** * @internal - * Updates Integrations - * -> calls 'updateMethod' in registered Integrations + * Updates registered and ready Integrations + * -> calls 'updateMethod' in all registered and ready Integrations * @param componentInstance - Component that gets updated - * @param updatedData - Updated Properties with new Value (Note: properties with no value won't get passed) + * @param updatedData - Properties that differ from the last Value */ public update(componentInstance: any, updatedData: Object): void { this.integrations.forEach((integration) => { - // Check if integration is ready if (!integration.ready) { - Agile.logger.warn( - `Agile: Integration '${integration.config.name}' isn't ready yet!` - ); + Agile.logger.warn(`Integration '${integration.key}' isn't ready yet!`); return; } - - if (integration.config.updateMethod) - integration.config.updateMethod(componentInstance, updatedData); + if (integration.methods.updateMethod) + integration.methods.updateMethod(componentInstance, updatedData); }); } @@ -76,7 +78,7 @@ export class Integrations { //========================================================================================================= /** * @internal - * Checks if Agile has registered any Integration + * Check if at least one Integration got registered */ public hasIntegration(): boolean { return this.integrations.size > 0; diff --git a/packages/core/src/integrations/integration.ts b/packages/core/src/integrations/integration.ts index 9e28e37c..d873882b 100644 --- a/packages/core/src/integrations/integration.ts +++ b/packages/core/src/integrations/integration.ts @@ -1,28 +1,60 @@ import { Agile } from "../internal"; -export class Integration { - public ready: boolean = false; - public config: IntegrationConfig; +export class Integration { + public _key: IntegrationKey; + public frameworkInstance?: F; + public ready = false; + public integrated = false; + public methods: IntegrationMethods; /** * @public - * Integration - Represents an Integration of Agile + * Integration - Represents a Framework/Integration of Agile * @param config - Config */ - constructor(config: IntegrationConfig) { - this.config = config; + constructor(config: CreateIntegrationConfig) { + this._key = config.key; + this.frameworkInstance = config.frameworkInstance; + this.methods = { + bind: config.bind, + updateMethod: config.updateMethod, + }; + } + + /** + * @public + * Set Value of Integration + */ + public set key(key: IntegrationKey) { + this._key = key; + } + + /** + * @public + * Get Value of Integration + */ + public get key(): IntegrationKey { + return this._key; } } /** - * @param name - Name of Integration - * @param frameworkInstance - An Instance of the Framework which gets integrated (for instance in case of react you pass React) - * @param bind - Will be called if the framework got successful integrated - * @param updateMethod - Will be called if a Observer updates his subs (Only by Component based Subscription) + * @param key - Key/Name of Integration + * @param frameworkInstance - An Instance of the Framework that this Integration represents (for instance React) */ -export interface IntegrationConfig { - name: string; +export interface CreateIntegrationConfig + extends IntegrationMethods { + key: string; frameworkInstance?: F; - bind?: (agileInstance: Agile) => boolean; - updateMethod?: (componentInstance: any, updatedData: Object) => void; } + +/** + * @param bind - Binds the Framework/Integration to Agile | Will be called after a successful integration + * @param updateMethod - Will be called if a Observer updates his subs (Only in Component based Subscriptions!) + */ +export interface IntegrationMethods { + bind?: (agileInstance: Agile) => Promise; + updateMethod?: (componentInstance: C, updatedData: Object) => void; +} + +export type IntegrationKey = string | number; diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index 1a76acf2..c427f8a3 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -16,11 +16,11 @@ export * from "./agile"; // Runtime export * from "./runtime"; export * from "./runtime/observer"; -export * from "./runtime/job"; +export * from "./runtime/runtime.job"; export * from "./runtime/subscription/container/SubscriptionContainer"; export * from "./runtime/subscription/container/CallbackSubscriptionContainer"; export * from "./runtime/subscription/container/ComponentSubscriptionContainer"; -export * from "./runtime/subscription/sub"; +export * from "./runtime/subscription/sub.controller"; // Storage export * from "./storages"; @@ -31,9 +31,11 @@ export * from "./storages/persistent"; export * from "./state"; export * from "./state/state.observer"; export * from "./state/state.persistent"; +export * from "./state/state.runtime.job"; // Computed export * from "./computed"; +export * from "./computed/computed.tracker"; // Collection export * from "./collection"; diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 4d92334b..d26ca8d5 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -1,26 +1,20 @@ import { Agile, SubscriptionContainer, - defineConfig, - Observer, - Job, - JobConfigInterface, + RuntimeJob, CallbackSubscriptionContainer, ComponentSubscriptionContainer, + defineConfig, } from "../internal"; export class Runtime { public agileInstance: () => Agile; // Queue system - private currentJob: Job | null = null; - private jobQueue: Array = []; - private notReadyJobsToRerender: Array = []; // Jobs that are performed but not ready to rerender (wait for mount) - private jobsToRerender: Array = []; // Jobs that are performed and will be rendered - - // Tracking - Used to track computed dependencies - public trackObservers: boolean = false; // Check if Runtime tracks Observers - public foundObservers: Set = new Set(); // Observers that got tracked (reset after stop tracking) + public currentJob: RuntimeJob | null = null; + public jobQueue: Array = []; + public notReadyJobsToRerender: Set = new Set(); // Jobs that got performed but aren't ready to get rerendered (wait for mount) + public jobsToRerender: Array = []; // Jobs that are performed and will be rendered /** * @internal @@ -36,29 +30,19 @@ export class Runtime { //========================================================================================================= /** * @internal - * Ingests Observer into Runtime - * -> Creates Job which will be performed by the Runtime - * @param observer - Observer that gets performed by the Runtime + * Ingests Job into Runtime that gets performed + * @param job - Job * @param config - Config */ - public ingest(observer: Observer, config: JobConfigInterface): void { - config = defineConfig(config, { + public ingest(job: RuntimeJob, config: IngestConfigInterface = {}): void { + config = defineConfig(config, { perform: true, - background: false, - sideEffects: true, - force: false, - storage: true, }); - const job = new Job(observer, config); + this.jobQueue.push(job); // Logging - Agile.logger.if - .tag(["runtime"]) - .info(`Created Job(${job.observer.key})`, job); - - // Add Job to JobQueue (-> no Job get missing) - this.jobQueue.push(job); + Agile.logger.if.tag(["runtime"]).info(`Created Job '${job._key}'`, job); // Perform Job if (config.perform) { @@ -72,10 +56,10 @@ export class Runtime { //========================================================================================================= /** * @internal - * Performs Job and adds him to the rerender queue if necessary + * Performs Job and adds it to the rerender queue if necessary * @param job - Job that gets performed */ - private perform(job: Job): void { + public perform(job: RuntimeJob): void { this.currentJob = job; // Perform Job @@ -86,11 +70,9 @@ export class Runtime { this.currentJob = null; // Logging - Agile.logger.if - .tag(["runtime"]) - .info(`Completed Job(${job.observer.key})`, job); + Agile.logger.if.tag(["runtime"]).info(`Completed Job '${job._key}'`, job); - // Perform Jobs as long as Jobs are in queue, if no job left update/rerender Subscribers of performed Jobs + // Perform Jobs as long as Jobs are left in queue, if no job left update/rerender Subscribers of jobsToRerender if (this.jobQueue.length > 0) { const performJob = this.jobQueue.shift(); if (performJob) this.perform(performJob); @@ -111,23 +93,33 @@ export class Runtime { * @internal * Updates/Rerenders all Subscribed Components of the Job (Observer) */ - private updateSubscribers(): void { - if (!this.agileInstance().integrations.hasIntegration()) { + public updateSubscribers(): boolean { + if (!this.agileInstance().hasIntegration()) { this.jobsToRerender = []; - return; + this.notReadyJobsToRerender = new Set(); + return false; } - if (this.jobsToRerender.length <= 0) return; - - // Subscriptions that has to be updated/rerendered (Set = For preventing double subscriptions without further checks) - const subscriptionsToUpdate: Set = new Set< - SubscriptionContainer - >(); + if ( + this.jobsToRerender.length <= 0 && + this.notReadyJobsToRerender.size <= 0 + ) + return false; + + // Subscriptions that has to be updated/rerendered + const subscriptionsToUpdate = new Set(); + + // Build final jobsToRerender and reset jobsToRerender Instances + const jobsToRerender = this.jobsToRerender.concat( + Array.from(this.notReadyJobsToRerender) + ); + this.notReadyJobsToRerender = new Set(); + this.jobsToRerender = []; // Check if Job Subscriptions are ready and add them to subscriptionsToUpdate - this.jobsToRerender.concat(this.notReadyJobsToRerender).forEach((job) => { - job.observer.subs.forEach((subscriptionContainer) => { + jobsToRerender.forEach((job) => { + job.subscriptionContainersToUpdate.forEach((subscriptionContainer) => { if (!subscriptionContainer.ready) { - this.notReadyJobsToRerender.push(job); + this.notReadyJobsToRerender.add(job); // Logging Agile.logger.warn( @@ -142,10 +134,10 @@ export class Runtime { this.handleObjectBasedSubscription(subscriptionContainer, job); subscriptionsToUpdate.add(subscriptionContainer); + job.subscriptionContainersToUpdate.delete(subscriptionContainer); }); }); - // Update Subscriptions that has to be updated/rerendered subscriptionsToUpdate.forEach((subscriptionContainer) => { // Call 'callback function' if Callback based Subscription if (subscriptionContainer instanceof CallbackSubscriptionContainer) @@ -164,7 +156,7 @@ export class Runtime { .tag(["runtime"]) .info("Updated/Rerendered Subscriptions", subscriptionsToUpdate); - this.jobsToRerender = []; + return true; } //========================================================================================================= @@ -172,25 +164,25 @@ export class Runtime { //========================================================================================================= /** * @internal - * Finds updated Key of SubscriptionContainer and adds it to 'changedObjectKeys' + * Finds key of Observer (Job) in subsObject and adds it to 'changedObjectKeys' * @param subscriptionContainer - Object based SubscriptionContainer - * @param job - Job that holds the SubscriptionContainer + * @param job - Job that holds the searched Observer */ public handleObjectBasedSubscription( subscriptionContainer: SubscriptionContainer, - job: Job + job: RuntimeJob ): void { - let localKey: string | null = null; + let foundKey: string | null = null; + // Check if SubscriptionContainer is Object based if (!subscriptionContainer.isObjectBased) return; - // Find localKey of Job Observer in SubscriptionContainer + // Find Key of Job Observer in SubscriptionContainer for (let key in subscriptionContainer.subsObject) if (subscriptionContainer.subsObject[key] === job.observer) - localKey = key; + foundKey = key; - // Add localKey to changedObjectKeys - if (localKey) subscriptionContainer.changedObjectKeys.push(localKey); + if (foundKey) subscriptionContainer.observerKeysToUpdate.push(foundKey); } //========================================================================================================= @@ -198,43 +190,32 @@ export class Runtime { //========================================================================================================= /** * @internal - * Builds Object from 'changedObjectKeys' with new Values provided by Observers - * @param subscriptionContainer - SubscriptionContainer from which the Object gets built + * Builds Object out of changedObjectKeys with Observer Value + * @param subscriptionContainer - Object based SubscriptionContainer */ public getObjectBasedProps( subscriptionContainer: SubscriptionContainer ): { [key: string]: any } { - const finalObject: { [key: string]: any } = {}; + const props: { [key: string]: any } = {}; - // Map trough changed Keys and build finalObject - subscriptionContainer.changedObjectKeys.forEach((changedKey) => { - // Check if Observer at changedKey has value property, if so add it to final Object + // Map trough observerKeysToUpdate and build object out of Observer value + subscriptionContainer.observerKeysToUpdate.forEach((updatedKey) => { if ( subscriptionContainer.subsObject && - subscriptionContainer.subsObject[changedKey]["value"] + subscriptionContainer.subsObject[updatedKey]["value"] ) - finalObject[changedKey] = - subscriptionContainer.subsObject[changedKey]["value"]; + props[updatedKey] = + subscriptionContainer.subsObject[updatedKey]["value"]; }); - subscriptionContainer.changedObjectKeys = []; - return finalObject; + subscriptionContainer.observerKeysToUpdate = []; + return props; } +} - //========================================================================================================= - // Get Tracked Observers - //========================================================================================================= - /** - * @internal - * Returns tracked Observers and stops Runtime from tracking anymore Observers - */ - public getTrackedObservers(): Set { - const finalFoundObservers = this.foundObservers; - - // Reset tracking - this.trackObservers = false; - this.foundObservers = new Set(); - - return finalFoundObservers; - } +/** + * @param perform - If Job gets performed immediately + */ +export interface IngestConfigInterface { + perform?: boolean; } diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts deleted file mode 100644 index e2d99903..00000000 --- a/packages/core/src/runtime/job.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Observer, defineConfig } from "../internal"; - -export class Job { - public observer: ObserverType; - public config: JobConfigInterface; - public rerender: boolean; // If Job will cause a rerender - public performed: boolean = false; // If Job has been performed by Runtime - - /** - * @internal - * Job - Holds Observer and gets executed/performed by the Runtime - * @param observer - Observer that is represented by this Job and gets performed - * @param config - Config - */ - constructor(observer: ObserverType, config: JobConfigInterface) { - this.config = defineConfig(config, { - background: false, - sideEffects: true, - force: false, - storage: true, - }); - this.config = config; - this.observer = observer; - this.rerender = - !config.background && - this.observer.agileInstance().integrations.hasIntegration(); - } -} - -/** - * @param background - If Job gets executed in the background -> not causing any rerender - * @param sideEffects - If SideEffects gets performed - * @param perform - If Job gets performed immediately - * @param storage - If Job value gets saved in Storage - * @param force - Force performing Job - */ -export interface JobConfigInterface { - background?: boolean; - sideEffects?: boolean; - perform?: boolean; - storage?: boolean; - force?: boolean; -} diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts index e03b2344..fd60f06a 100644 --- a/packages/core/src/runtime/observer.ts +++ b/packages/core/src/runtime/observer.ts @@ -1,4 +1,10 @@ -import { Agile, StateKey, Job, SubscriptionContainer } from "../internal"; +import { + Agile, + StateKey, + RuntimeJob, + SubscriptionContainer, + defineConfig, +} from "../internal"; export type ObserverKey = string | number; @@ -7,27 +13,31 @@ export class Observer { public _key?: ObserverKey; public deps: Set = new Set(); // Observers that depends on this Observer - public subs: Set = new Set(); // SubscriptionContainers(Components) which this Observer has subscribed + public subs: Set = new Set(); // SubscriptionContainers (Components) that this Observer has subscribed public value?: ValueType; // Value of Observer /** * @internal * Observer - Handles subscriptions and dependencies of an Agile Class and is like an instance to the Runtime + * Note: No stand alone class!! * @param agileInstance - An instance of Agile - * @param value - Initial Value of Observer - * @param deps - Initial Dependencies of Observer - * @param key - Key/Name of Observer + * @param config - Config */ constructor( agileInstance: Agile, - deps?: Array, - key?: ObserverKey, - value?: ValueType + config: CreateObserverConfigInterface = {} ) { + config = defineConfig(config, { + deps: [], + subs: [], + }); this.agileInstance = () => agileInstance; - this._key = key; - this.value = value; - deps?.forEach((observer) => this.deps.add(observer)); + this._key = config.key; + this.value = config.value; + config.deps?.forEach((observer) => this.depend(observer)); + config.subs?.forEach((subscriptionContainer) => + this.subscribe(subscriptionContainer) + ); } /** @@ -54,8 +64,10 @@ export class Observer { * Performs Job of Runtime * @param job - Job that gets performed */ - public perform(job: Job) { - Agile.logger.warn("Didn't set perform function in Observer ", this.key); + public perform(job: RuntimeJob) { + Agile.logger.warn( + "Perform function isn't Set in Observer! Be aware that Observer is no stand alone class!" + ); } //========================================================================================================= @@ -79,8 +91,12 @@ export class Observer { * @param subscriptionContainer - SubscriptionContainer(Component) that gets subscribed by this Observer */ public subscribe(subscriptionContainer: SubscriptionContainer) { - if (!this.subs.has(subscriptionContainer)) + if (!this.subs.has(subscriptionContainer)) { this.subs.add(subscriptionContainer); + + // Add this to subscriptionContainer to keep track of the Observers the subscriptionContainer hold + subscriptionContainer.subs.add(this); + } } //========================================================================================================= @@ -92,7 +108,22 @@ export class Observer { * @param subscriptionContainer - SubscriptionContainer(Component) that gets unsubscribed by this Observer */ public unsubscribe(subscriptionContainer: SubscriptionContainer) { - if (this.subs.has(subscriptionContainer)) + if (this.subs.has(subscriptionContainer)) { this.subs.delete(subscriptionContainer); + subscriptionContainer.subs.delete(this); + } } } + +/** + * @param deps - Initial Dependencies of Observer + * @param subs - Initial Subscriptions of Observer + * @param key - Key/Name of Observer + * @param value - Initial Value of Observer + */ +export interface CreateObserverConfigInterface { + deps?: Array; + subs?: Array; + key?: ObserverKey; + value?: ValueType; +} diff --git a/packages/core/src/runtime/runtime.job.ts b/packages/core/src/runtime/runtime.job.ts new file mode 100644 index 00000000..7e80e2a1 --- /dev/null +++ b/packages/core/src/runtime/runtime.job.ts @@ -0,0 +1,67 @@ +import { Observer, defineConfig, SubscriptionContainer } from "../internal"; + +export class RuntimeJob { + public _key?: RuntimeJobKey; + public observer: ObserverType; + public config: RuntimeJobConfigInterface; + public rerender: boolean; // If Job will cause rerender on subscriptionContainer in Observer + public performed = false; // If Job has been performed by Runtime + public subscriptionContainersToUpdate = new Set(); // SubscriptionContainer that have to be updated/rerendered + + /** + * @internal + * Job - Represents Observer that gets performed by the Runtime + * @param observer - Observer + * @param config - Config + */ + constructor( + observer: ObserverType, + config: CreateRuntimeJobConfigInterface = {} + ) { + config = defineConfig(config, { + background: false, + sideEffects: true, + force: false, + }); + this.config = { + background: config.background, + force: config.force, + sideEffects: config.sideEffects, + }; + this.observer = observer; + this.rerender = + !config.background && + this.observer.agileInstance().integrations.hasIntegration(); + this._key = config.key; + this.subscriptionContainersToUpdate = new Set(observer.subs); + } + + public get key(): RuntimeJobKey | undefined { + return this._key; + } + + public set key(value: RuntimeJobKey | undefined) { + this._key = value; + } +} + +export type RuntimeJobKey = string | number; + +/** + * @param key - Key/Name of RuntimeJob + */ +export interface CreateRuntimeJobConfigInterface + extends RuntimeJobConfigInterface { + key?: RuntimeJobKey; +} + +/** + * @param background - If Job gets executed in the background -> not causing any rerender + * @param sideEffects - If SideEffects get executed + * @param force - Force performing Job + */ +export interface RuntimeJobConfigInterface { + background?: boolean; + sideEffects?: boolean; + force?: boolean; +} diff --git a/packages/core/src/runtime/subscription/container/CallbackSubscriptionContainer.ts b/packages/core/src/runtime/subscription/container/CallbackSubscriptionContainer.ts index d579e5e5..b83fe48b 100644 --- a/packages/core/src/runtime/subscription/container/CallbackSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/container/CallbackSubscriptionContainer.ts @@ -16,7 +16,7 @@ export class CallbackSubscriptionContainer extends SubscriptionContainer { */ constructor( callback: Function, - subs?: Set, + subs: Array = [], key?: SubscriptionContainerKeyType ) { super(subs, key); diff --git a/packages/core/src/runtime/subscription/container/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/container/ComponentSubscriptionContainer.ts index a066a0bb..e6ba85de 100644 --- a/packages/core/src/runtime/subscription/container/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/container/ComponentSubscriptionContainer.ts @@ -16,7 +16,7 @@ export class ComponentSubscriptionContainer extends SubscriptionContainer { */ constructor( component: any, - subs?: Set, + subs: Array = [], key?: SubscriptionContainerKeyType ) { super(subs, key); diff --git a/packages/core/src/runtime/subscription/container/SubscriptionContainer.ts b/packages/core/src/runtime/subscription/container/SubscriptionContainer.ts index 389b4115..d22f4c50 100644 --- a/packages/core/src/runtime/subscription/container/SubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/container/SubscriptionContainer.ts @@ -2,13 +2,13 @@ import { Observer } from "../../../internal"; export class SubscriptionContainer { public key?: SubscriptionContainerKeyType; - public ready: boolean = false; - public subs: Set = new Set([]); // Observers that are Subscribed to this SubscriptionContainer (Component) + public ready = false; + public subs: Set; // Observers that are Subscribed to this SubscriptionContainer (Component) // For Object based Subscription - public isObjectBased: boolean = false; - public changedObjectKeys: Array = []; // Holds temporary changed Object Keys (Runtime) - public subsObject?: { [key: string]: Observer }; // Same as subs but in Object form + public isObjectBased = false; + public observerKeysToUpdate: Array = []; // Holds temporary keys of Observers that got updated (Note: keys based on 'subsObject') + public subsObject?: { [key: string]: Observer }; // Same as subs but in Object shape /** * @internal @@ -17,8 +17,8 @@ export class SubscriptionContainer { * @param subs - Initial Subscriptions * @param key - Key/Name of Subscription Container */ - constructor(subs?: Set, key?: SubscriptionContainerKeyType) { - if (subs) this.subs = subs; + constructor(subs: Array = [], key?: SubscriptionContainerKeyType) { + this.subs = new Set(subs); this.key = key; } } diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.controller.ts similarity index 76% rename from packages/core/src/runtime/subscription/sub.ts rename to packages/core/src/runtime/subscription/sub.controller.ts index 55f0b3f3..d837810a 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.controller.ts @@ -14,6 +14,8 @@ export class SubController { public componentSubs: Set = new Set(); // Holds all registered Component based Subscriptions public callbackSubs: Set = new Set(); // Holds all registered Callback based Subscriptions + public mountedComponents: Set = new Set(); // Holds all mounted Components (only if agileInstance.config.mount = true) + /** * @internal * SubController - Handles subscriptions to Components @@ -41,35 +43,27 @@ export class SubController { subscriptionContainer: SubscriptionContainer; props: { [key: string]: Observer["value"] }; } { - const props: { [key: string]: Observer } = {}; + const props: { [key: string]: Observer["value"] } = {}; // Create subsArray const subsArray: Observer[] = []; - for (let key in subs) { - subsArray.push(subs[key]); - } + for (let key in subs) subsArray.push(subs[key]); // Register Subscription -> decide weather subscriptionInstance is callback or component based const subscriptionContainer = this.registerSubscription( integrationInstance, - subsArray + subsArray, + key ); // Set SubscriptionContainer to Object based subscriptionContainer.isObjectBased = true; subscriptionContainer.subsObject = subs; - // Register subs + // Register subs and build props object for (let key in subs) { const observer = subs[key]; - - // Add Observer to SubscriptionContainer Subs - subscriptionContainer.subs.add(observer); - - // Add SubscriptionContainer to Observer Subs observer.subscribe(subscriptionContainer); - - // Add Value to props if Observer has value if (observer.value) props[key] = observer.value; } @@ -102,15 +96,7 @@ export class SubController { ); // Register subs - subs.forEach((observable) => { - if (!observable) return; - - // Add Observer to SubscriptionContainer Subs - subscriptionContainer.subs.add(observable); - - // Add SubscriptionContainer to Observer Subs - observable.subscribe(subscriptionContainer); - }); + subs.forEach((observer) => observer.subscribe(subscriptionContainer)); return subscriptionContainer; } @@ -124,11 +110,11 @@ export class SubController { * @param subscriptionInstance - SubscriptionContainer or Component that holds an SubscriptionContainer */ public unsubscribe(subscriptionInstance: any) { - // Helper function to unsubscribe callback or component based subscription + // Helper function to remove SubscriptionContainer from Observer const unsub = (subscriptionContainer: SubscriptionContainer) => { subscriptionContainer.ready = false; - // Removes SubscriptionContainer from Observer subs + // Remove SubscriptionContainers from Observer subscriptionContainer.subs.forEach((observer) => { observer.unsubscribe(subscriptionContainer); }); @@ -143,26 +129,44 @@ export class SubController { Agile.logger.if .tag(["core", "subscription"]) .info( - "Agile: Unregistered Callback based Subscription ", + "Unregistered Callback based Subscription ", subscriptionInstance ); + return; } // Unsubscribe component based Subscription - // Check if component/class has property componentSubscriptionContainer, which holds an instance of ComponentSubscriptionContainer + if (subscriptionInstance instanceof ComponentSubscriptionContainer) { + unsub(subscriptionInstance); + this.componentSubs.delete(subscriptionInstance); + + // Logging + Agile.logger.if + .tag(["core", "subscription"]) + .info( + "Unregistered Component based Subscription ", + subscriptionInstance + ); + return; + } + + // Unsubscribe component based Subscription with subscriptionInstance that holds a componentSubscriptionContainer if (subscriptionInstance.componentSubscriptionContainer) { unsub( subscriptionInstance.componentSubscriptionContainer as ComponentSubscriptionContainer ); - this.componentSubs.delete(subscriptionInstance); + this.componentSubs.delete( + subscriptionInstance.componentSubscriptionContainer + ); // Logging Agile.logger.if .tag(["core", "subscription"]) .info( - "Agile: Unregistered Component based Subscription ", + "Unregistered Component based Subscription ", subscriptionInstance ); + return; } } @@ -196,31 +200,32 @@ export class SubController { * @param subs - Initial Subscriptions * @param key - Key/Name of SubscriptionContainer */ - private registerComponentSubscription( + public registerComponentSubscription( componentInstance: any, subs: Array = [], key?: SubscriptionContainerKeyType ): ComponentSubscriptionContainer { const componentSubscriptionContainer = new ComponentSubscriptionContainer( componentInstance, - new Set(subs), + subs, key ); this.componentSubs.add(componentSubscriptionContainer); - // To have an instance of a SubscriptionContainer in the Component (needed to unsubscribe component later) - if (componentInstance.componentSubscriptionContainer) - componentInstance.componentSubscriptionContainer = componentSubscriptionContainer; - // Set to ready if not waiting for component to mount - if (!this.agileInstance().config.waitForMount) - componentSubscriptionContainer.ready = true; + if (this.agileInstance().config.waitForMount) { + if (this.mountedComponents.has(componentInstance)) + componentSubscriptionContainer.ready = true; + } else componentSubscriptionContainer.ready = true; + + // To have an instance of the SubscriptionContainer in the Component (necessary to unsubscribe component later) + componentInstance.componentSubscriptionContainer = componentSubscriptionContainer; // Logging Agile.logger.if .tag(["core", "subscription"]) .info( - "Agile: Registered Component based Subscription ", + "Registered Component based Subscription ", componentSubscriptionContainer ); @@ -237,14 +242,14 @@ export class SubController { * @param subs - Initial Subscriptions * @param key - Key/Name of SubscriptionContainer */ - private registerCallbackSubscription( + public registerCallbackSubscription( callbackFunction: () => void, subs: Array = [], key?: SubscriptionContainerKeyType ): CallbackSubscriptionContainer { const callbackSubscriptionContainer = new CallbackSubscriptionContainer( callbackFunction, - new Set(subs), + subs, key ); this.callbackSubs.add(callbackSubscriptionContainer); @@ -254,7 +259,7 @@ export class SubController { Agile.logger.if .tag(["core", "subscription"]) .info( - "Agile: Registered Callback based Subscription ", + "Registered Callback based Subscription ", callbackSubscriptionContainer ); @@ -270,7 +275,24 @@ export class SubController { * @param componentInstance - SubscriptionContainer(Component) that gets mounted */ public mount(componentInstance: any) { - if (!componentInstance.componentSubscriptionContainer) return; - componentInstance.componentSubscriptionContainer.ready = true; + if (componentInstance.componentSubscriptionContainer) + componentInstance.componentSubscriptionContainer.ready = true; + + this.mountedComponents.add(componentInstance); + } + + //========================================================================================================= + // Unmount + //========================================================================================================= + /** + * @internal + * Unmounts Component based SubscriptionContainer + * @param componentInstance - SubscriptionContainer(Component) that gets unmounted + */ + public unmount(componentInstance: any) { + if (componentInstance.componentSubscriptionContainer) + componentInstance.componentSubscriptionContainer.ready = false; + + this.mountedComponents.delete(componentInstance); } } diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index bea96ba5..f87a80e7 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -12,8 +12,10 @@ import { isFunction, notEqual, generateId, - StatePersistentConfigInterface, - JobConfigInterface, + PersistentKey, + ComputedTracker, + StateIngestConfigInterface, + StateRuntimeJobConfigInterface, } from "../internal"; export class State { @@ -21,8 +23,8 @@ export class State { public _key?: StateKey; public valueType?: string; // primitive Type of State Value (for JS users) - public isSet: boolean = false; // If value is not the same as initialValue - public isPlaceholder: boolean = false; + public isSet = false; // If value is not the same as initialValue + public isPlaceholder = false; public initialStateValue: ValueType; public _value: ValueType; // Current Value of State public previousStateValue: ValueType; @@ -34,7 +36,7 @@ export class State { } = {}; // SideEffects of State (will be executed in Runtime) public computeMethod?: ComputeMethod; - public isPersisted: boolean = false; // If State can be stored in Agile Storage (-> successfully integrated persistent) + public isPersisted = false; // If State can be stored in Agile Storage (-> successfully integrated persistent) public persistent: StatePersistent | undefined; // Manages storing State Value into Storage public watchers: { [key: string]: StateWatcherCallback } = {}; @@ -44,27 +46,31 @@ export class State { * State - Class that holds one Value and causes rerender on subscribed Components * @param agileInstance - An instance of Agile * @param initialValue - Initial Value of State - * @param key - Key/Name of State - * @param deps - Initial deps of State + * @param config - Config */ constructor( agileInstance: Agile, initialValue: ValueType, - key?: StateKey, - deps: Array = [] + config: StateConfigInterface = {} ) { + config = defineConfig(config, { + deps: [], + isPlaceholder: false, + }); this.agileInstance = () => agileInstance; - this.initialStateValue = initialValue; - this._key = key; + this._key = config.key; + this.observer = new StateObserver(this, { + key: config.key, + deps: config.deps, + }); + this.initialStateValue = copy(initialValue); this._value = copy(initialValue); this.previousStateValue = copy(initialValue); this.nextStateValue = copy(initialValue); - this.observer = new StateObserver( - agileInstance, - this, - deps, - key - ); + this.isPlaceholder = true; + + // Initial Set + if (!config.isPlaceholder) this.set(initialValue, { overwrite: true }); } /** @@ -80,10 +86,7 @@ export class State { * Get Value of State */ public get value(): ValueType { - // Add State to tracked Observers (for auto tracking used observers in computed function) - if (this.agileInstance().runtime.trackObservers) - this.agileInstance().runtime.foundObservers.add(this.observer); - + ComputedTracker.tracked(this.observer); return this._value; } @@ -108,26 +111,23 @@ export class State { //========================================================================================================= /** * @internal - * Set Key/Name of State - * https://github.com/microsoft/TypeScript/issues/338 + * Updates Key/Name of State * @param value - New Key/Name of State */ - public setKey(value: StateKey | undefined) { + public setKey(value: StateKey | undefined): this { const oldKey = this._key; // Update State Key this._key = value; // Update Key in Observer - this.observer.key = value; - - // Update Key in PersistManager - if ( - value !== undefined && - this.persistent && - this.persistent.key === oldKey - ) - this.persistent.key = value; + this.observer._key = value; + + // Update Key in Persistent (only if oldKey equal to persistentKey -> otherwise the PersistentKey got formatted and will be set where other) + if (value && this.persistent?._key === oldKey) + this.persistent?.setKey(value); + + return this; } //========================================================================================================= @@ -139,25 +139,30 @@ export class State { * @param value - new State Value * @param config - Config */ - public set(value: ValueType, config: SetConfigInterface = {}): this { + public set( + value: ValueType, + config: StateRuntimeJobConfigInterface = {} + ): this { config = defineConfig(config, { sideEffects: true, background: false, force: false, storage: true, + overwrite: false, }); // Check value has correct Type (js) - if (this.valueType && !this.hasCorrectType(value)) { - Agile.logger.error(`Incorrect type (${typeof value}) was provided.`); - return this; + if (!this.hasCorrectType(value)) { + const message = `Incorrect type (${typeof value}) was provided.`; + if (!config.force) { + Agile.logger.error(message); + return this; + } + Agile.logger.warn(message); } - // Check if value has changed - if (equal(this.nextStateValue, value) && !config.force) return this; - - // Ingest new value into runtime - this.observer.ingest(value, config); + // Ingest new value into Runtime + this.observer.ingestValue(value, config); return this; } @@ -170,12 +175,7 @@ export class State { * Ingests nextStateValue, computedValue into Runtime * @param config - Config */ - public ingest(config: JobConfigInterface = {}): this { - config = defineConfig(config, { - sideEffects: true, - background: false, - force: false, - }); + public ingest(config: StateIngestConfigInterface = {}): this { this.observer.ingest(config); return this; } @@ -210,9 +210,11 @@ export class State { /** * @public * Undoes latest State Value change + * @param config - Config */ - public undo() { - this.set(this.previousStateValue); + public undo(config: StateRuntimeJobConfigInterface = {}): this { + this.set(this.previousStateValue, config); + return this; } //========================================================================================================= @@ -223,9 +225,8 @@ export class State { * Resets State to its initial Value * @param config - Config */ - public reset(config: SetConfigInterface = {}): this { + public reset(config: StateRuntimeJobConfigInterface = {}): this { this.set(this.initialStateValue, config); - this.persistent?.removeValue(); // Remove State Value from Storage (since its the initial Value) return this; } @@ -245,40 +246,46 @@ export class State { ): this { config = defineConfig(config, { addNewProperties: true, + sideEffects: true, background: false, + force: false, + storage: true, + overwrite: false, }); if (!isValidObject(this.nextStateValue)) { Agile.logger.error( - "You can't use the patch method on a non object States!" + "You can't use the patch method on a non object based States!" ); return this; } if (!isValidObject(targetWithChanges)) { - Agile.logger.error("TargetWithChanges has to be an object!"); + Agile.logger.error("TargetWithChanges has to be an Object!"); return this; } // Merge targetWithChanges into nextStateValue this.nextStateValue = flatMerge( - this.nextStateValue, + copy(this.nextStateValue), targetWithChanges, { addNewProperties: config.addNewProperties } ); - // Check if value has been changed - if (equal(this.value, this.nextStateValue)) return this; - // Ingest updated nextStateValue into Runtime - this.ingest({ background: config.background }); + this.ingest({ + background: config.background, + force: config.force, + overwrite: config.overwrite, + sideEffects: config.sideEffects, + storage: config.storage, + }); return this; } //========================================================================================================= // Watch - // https://stackoverflow.com/questions/12688275/is-there-a-way-to-do-method-overloading-in-typescript/12689054#12689054 //========================================================================================================= /** * @public @@ -290,7 +297,7 @@ export class State { /** * @public * Watches State and detects State changes - * @param key - Key of Watcher Function + * @param key - Key/Name of Watcher Function * @param callback - Callback Function that gets called if the State Value changes */ public watch(key: string, callback: StateWatcherCallback): this; @@ -310,16 +317,18 @@ export class State { _callback = callback as StateWatcherCallback; } - // Check if Callback is a Function + // Check if Callback is valid Function if (!isFunction(_callback)) { - Agile.logger.error("A Watcher Callback Function has to be an function!"); + Agile.logger.error( + "A Watcher Callback Function has to be typeof Function!" + ); return this; } - // Check if Callback Function already exists + // Check if watcherKey is already occupied if (this.watchers[key]) { Agile.logger.error( - `Watcher Callback Function with the key/name ${key} already exists!` + `Watcher Callback Function with the key/name '${key}' already exists!` ); return this; } @@ -347,9 +356,9 @@ export class State { * @param callback - Callback Function that gets called if the State Value changes */ public onInaugurated(callback: StateWatcherCallback) { - const watcherKey = "InauguratedWatcher"; - this.watch(watcherKey, () => { - callback(this.getPublicValue()); + const watcherKey = "InauguratedWatcherKey"; + this.watch(watcherKey, (value) => { + callback(value); this.removeWatcher(watcherKey); }); } @@ -360,7 +369,7 @@ export class State { /** * @public * Checks if watcher at given Key exists - * @param key - Key of Watcher + * @param key - Key/Name of Watcher */ public hasWatcher(key: string): boolean { return !!this.watchers[key]; @@ -378,44 +387,43 @@ export class State { /** * @public * Stores State Value into Agile Storage permanently - * @param key - Storage Key (Note: not needed if State has key/name) + * @param key - Key/Name of created Persistent (Note: Key required if State has no set Key!) * @param config - Config */ public persist( - key?: StorageKey, + key?: PersistentKey, config?: StatePersistentConfigInterface ): this; public persist( - keyOrConfig: StorageKey | StatePersistentConfigInterface = {}, + keyOrConfig: PersistentKey | StatePersistentConfigInterface = {}, config: StatePersistentConfigInterface = {} ): this { let _config: StatePersistentConfigInterface; - let key: StorageKey | undefined; + let key: PersistentKey | undefined; if (isValidObject(keyOrConfig)) { _config = keyOrConfig as StatePersistentConfigInterface; - key = undefined; + key = this._key; } else { _config = config || {}; - key = keyOrConfig as StorageKey; + key = keyOrConfig as PersistentKey; } _config = defineConfig(_config, { instantiate: true, - storageKeys: undefined, + storageKeys: [], }); - // Update Persistent Key - if (this.persistent) { - this.persistent.storageKeys = config.storageKeys; - if (key) this.persistent.setKey(key); - return this; - } + if (this.persistent) + Agile.logger.warn( + `By persisting the State '${this._key}' twice you overwrite the old Persistent Instance!` + ); // Create persistent -> Persist Value - this.persistent = new StatePersistent(this, key, { + this.persistent = new StatePersistent(this, { instantiate: _config.instantiate, storageKeys: _config.storageKeys, + key: key, }); return this; @@ -426,7 +434,7 @@ export class State { //========================================================================================================= /** * @public - * Callback Function gets called if persisted Value gets loaded into the State for the first Time + * Callback Function that gets called if the persisted Value gets loaded into the State for the first Time * Note: Only useful for persisted States! * @param callback - Callback Function */ @@ -434,11 +442,11 @@ export class State { if (this.persistent) { this.persistent.onLoad = callback; - // If State isPersisted the loading was successful -> callback can be called + // If State is already 'isPersisted' the loading was successful -> callback can be called if (this.isPersisted) callback(true); } else { - Agile.logger.warn( - `Please make sure you persist the State '${this.key}' before using onLoad!` + Agile.logger.error( + `Please make sure you persist the State '${this._key}' before using the 'onLoad' function!` ); } return this; @@ -463,7 +471,7 @@ export class State { * Checks if State exists */ public get exists(): boolean { - return this.getPublicValue() !== undefined && !this.isPlaceholder; + return this._value !== undefined && !this.isPlaceholder; } //========================================================================================================= @@ -499,11 +507,11 @@ export class State { * Note: Only useful with boolean based States */ public invert(): this { - if (typeof this._value !== "boolean") { + if (typeof this._value === "boolean") { + this.set(!this._value as any); + } else { Agile.logger.error("You can only invert boolean based States!"); - return this; } - this.set(this._value); return this; } @@ -512,10 +520,14 @@ export class State { //========================================================================================================= /** * @public - * Compute Value if it changes - * @param method - Method that will be used to compute the new Value + * Function that recomputes State Value if it changes + * @param method - Computed Function */ public compute(method: ComputeMethod): this { + if (!isFunction(method)) { + Agile.logger.error("A computeMethod has to be a function!"); + return this; + } this.computeMethod = method; return this; } @@ -526,7 +538,7 @@ export class State { /** * @internal * Adds SideEffect to State - * @param key - Key of SideEffect + * @param key - Key/Name of SideEffect * @param sideEffect - Callback Function that gets called on every State Value change */ public addSideEffect( @@ -534,7 +546,7 @@ export class State { sideEffect: (properties?: { [key: string]: any }) => void ): this { if (!isFunction(sideEffect)) { - Agile.logger.error("A sideEffect function has to be an function!"); + Agile.logger.error("A sideEffect function has to be a function!"); return this; } this.sideEffects[key] = sideEffect; @@ -572,9 +584,11 @@ export class State { /** * @internal * Checks if Value has correct valueType (js) + * Note: If no valueType set, it returns true * @param value - Value that gets checked for its correct Type */ - private hasCorrectType(value: any): boolean { + public hasCorrectType(value: any): boolean { + if (!this.valueType) return true; let type: string = typeof value; return type === this.valueType; } @@ -601,32 +615,37 @@ export class State { * Returns Value that gets written into the Agile Storage */ public getPersistableValue(): any { - return this.value; + return this._value; } } export type StateKey = string | number; /** - * @param background - If assigning a new value happens in the background (-> not causing any rerender) - * @param sideEffects - If Side Effects of State get executed - * @param storage - If State value gets saved in Agile Storage (only useful if State is persisted) - * @param force - Force creating and performing Job + * @param key - Key/Name of State + * @param deps - Initial deps of State + * @param isPlaceholder - If State is initially a Placeholder */ -export interface SetConfigInterface { - background?: boolean; - sideEffects?: boolean; - storage?: boolean; - force?: boolean; +export interface StateConfigInterface { + key?: StateKey; + deps?: Array; + isPlaceholder?: boolean; } /** - * @param background - If assigning new value happens in the background (-> not causing any rerender) * @param addNewProperties - If new Properties gets added to the State Value */ -export interface PatchConfigInterface { +export interface PatchConfigInterface extends StateRuntimeJobConfigInterface { addNewProperties?: boolean; - background?: boolean; +} + +/** + * @param instantiate - If Persistent gets instantiated + * @param storageKeys - Key/Name of Storages which gets used to persist the State Value (NOTE: If not passed the default Storage will be used) + */ +export interface StatePersistentConfigInterface { + instantiate?: boolean; + storageKeys?: StorageKey[]; } export type StateWatcherCallback = (value: T) => void; diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts index c1faa4a9..4932cef6 100644 --- a/packages/core/src/state/state.observer.ts +++ b/packages/core/src/state/state.observer.ts @@ -1,16 +1,18 @@ import { - Agile, Observer, State, Computed, - Job, - JobConfigInterface, copy, defineConfig, ObserverKey, equal, notEqual, isFunction, + SubscriptionContainer, + IngestConfigInterface, + StateRuntimeJob, + StateRuntimeJobConfigInterface, + RuntimeJobKey, } from "../internal"; export class StateObserver extends Observer { @@ -20,20 +22,16 @@ export class StateObserver extends Observer { /** * @internal * State Observer - Handles State changes, dependencies (-> Interface to Runtime) - * @param agileInstance - An instance of Agile * @param state - State - * @param deps - Initial Dependencies of State Observer - * @param key - Key/Name of State Observer + * @param config - Config */ constructor( - agileInstance: Agile, state: State, - deps?: Array, - key?: ObserverKey + config: CreateStateObserverConfigInterface = {} ) { - super(agileInstance, deps, key, state.value); + super(state.agileInstance(), { ...config, ...{ value: state._value } }); this.state = () => state; - this.nextStateValue = copy(state.value); + this.nextStateValue = copy(state._value); } //========================================================================================================= @@ -41,51 +39,69 @@ export class StateObserver extends Observer { //========================================================================================================= /** * @internal - * Ingests nextStateValue into Runtime and applies it to the State + * Ingests nextStateValue or computedValue into Runtime and applies it to the State * @param config - Config */ - public ingest(config: JobConfigInterface): void; + public ingest(config: StateIngestConfigInterface = {}): void { + const state = this.state(); + let newStateValue: ValueType; + + if (state instanceof Computed) newStateValue = state.computeValue(); + else newStateValue = state.nextStateValue; + + this.ingestValue(newStateValue, config); + } + + //========================================================================================================= + // Ingest Value + //========================================================================================================= /** * @internal * Ingests new State Value into Runtime and applies it to the State * @param newStateValue - New Value of the State * @param config - Config */ - public ingest(newStateValue: ValueType, config: JobConfigInterface): void; - public ingest( - newStateValueOrConfig: ValueType | JobConfigInterface, - config: JobConfigInterface = {} + public ingestValue( + newStateValue: ValueType, + config: StateIngestConfigInterface = {} ): void { const state = this.state(); - let _newStateValue: ValueType; - let _config: JobConfigInterface; - - if (isStateJobConfigInterface(newStateValueOrConfig)) { - _config = newStateValueOrConfig; - if (state instanceof Computed) _newStateValue = state.computeValue(); - else _newStateValue = state.nextStateValue; - } else { - _config = config; - _newStateValue = newStateValueOrConfig; - } - - _config = defineConfig(_config, { + config = defineConfig(config, { perform: true, background: false, sideEffects: true, force: false, storage: true, + overwrite: false, }); + // Force overwriting State because if assigning Value to State, the State shouldn't be a placeholder anymore + if (state.isPlaceholder) { + config.force = true; + config.overwrite = true; + } + // Assign next State Value and compute it if necessary this.nextStateValue = state.computeMethod - ? copy(state.computeMethod(_newStateValue)) - : copy(_newStateValue); + ? copy(state.computeMethod(newStateValue)) + : copy(newStateValue); // Check if State Value and new/next Value are equals - if (equal(state.value, this.nextStateValue) && !_config.force) return; + if (equal(state._value, this.nextStateValue) && !config.force) return; + + // Create Job + const job = new StateRuntimeJob(this, { + storage: config.storage, + sideEffects: config.sideEffects, + force: config.force, + background: config.background, + overwrite: config.overwrite, + key: config.key || this._key, + }); - this.agileInstance().runtime.ingest(this, _config); + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -93,37 +109,27 @@ export class StateObserver extends Observer { //========================================================================================================= /** * @internal - * Performs Job from Runtime - * @param job - Job that gets performed + * Performs Job that holds this Observer + * @param job - Job */ - public perform(job: Job) { + public perform(job: StateRuntimeJob) { const state = job.observer.state(); - // Set Previous State - state.previousStateValue = copy(state.value); - - // Set new State Value - state._value = copy(this.nextStateValue); - state.nextStateValue = copy(this.nextStateValue); + // Assign new State Values + state.previousStateValue = copy(state._value); + state._value = copy(job.observer.nextStateValue); + state.nextStateValue = copy(job.observer.nextStateValue); + job.observer.value = copy(job.observer.nextStateValue); - // Store State changes in Storage - if (job.config.storage && state.isPersisted) - state.persistent?.updateValue(); - - // Set isSet - state.isSet = notEqual(this.nextStateValue, state.initialStateValue); - - // Reset isPlaceholder and set initial/previous Value to nextValue because the placeholder State had no proper value before - if (state.isPlaceholder) { - state.initialStateValue = copy(state.value); - state.previousStateValue = copy(state.value); + // Overwrite old State Values + if (job.config.overwrite) { + state.initialStateValue = copy(state._value); + state.previousStateValue = copy(state._value); state.isPlaceholder = false; } - // Update Observer value - this.value = copy(this.nextStateValue); + state.isSet = notEqual(state._value, state.initialStateValue); - // Perform SideEffects of the Perform Function this.sideEffects(job); } @@ -132,10 +138,10 @@ export class StateObserver extends Observer { //========================================================================================================= /** * @internal - * SideEffects of Perform Function - * @param job - Job whose SideEffects gets executed + * SideEffects of Job + * @param job - Job */ - private sideEffects(job: Job) { + public sideEffects(job: StateRuntimeJob) { const state = job.observer.state(); // Call Watchers Functions @@ -150,24 +156,29 @@ export class StateObserver extends Observer { state.sideEffects[sideEffectKey](job.config); // Ingest Dependencies of Observer into Runtime - job.observer.deps.forEach( + state.observer.deps.forEach( (observer) => observer instanceof StateObserver && observer.ingest({ perform: false }) ); } } -// https://stackoverflow.com/questions/40081332/what-does-the-is-keyword-do-in-typescript -export function isStateJobConfigInterface( - object: any -): object is JobConfigInterface { - return ( - object && - typeof object === "object" && - ("force" in object || - "perform" in object || - "background" in object || - "sideEffects" in object || - "storage" in object) - ); +/** + * @param deps - Initial Dependencies of State Observer + * @param subs - Initial Subscriptions of State Observer + * @param key - Key/Name of State Observer + */ +export interface CreateStateObserverConfigInterface { + deps?: Array; + subs?: Array; + key?: ObserverKey; +} + +/** + * @param key - Key/Name of Job that gets created + */ +export interface StateIngestConfigInterface + extends StateRuntimeJobConfigInterface, + IngestConfigInterface { + key?: RuntimeJobKey; } diff --git a/packages/core/src/state/state.persistent.ts b/packages/core/src/state/state.persistent.ts index 8f7dc1f5..5e1fcf73 100644 --- a/packages/core/src/state/state.persistent.ts +++ b/packages/core/src/state/state.persistent.ts @@ -1,146 +1,207 @@ -import { defineConfig, Persistent, State, StorageKey } from "../internal"; +import { + CreatePersistentConfigInterface, + defineConfig, + Persistent, + PersistentKey, + State, + StorageKey, +} from "../internal"; export class StatePersistent extends Persistent { + static storeValueSideEffectKey = "rebuildStateStorageValue"; public state: () => State; /** * @internal * State Persist Manager - Handles permanent storing of State Value * @param state - State that gets stored - * @param key - Key of Storage property * @param config - Config */ constructor( state: State, - key?: StorageKey, - config: StatePersistentConfigInterface = {} + config: CreatePersistentConfigInterface = {} ) { - super(state.agileInstance(), config.storageKeys); + super(state.agileInstance(), { + instantiate: false, + }); config = defineConfig(config, { instantiate: true, + storageKeys: [], }); this.state = () => state; - this.storageKeys = config.storageKeys; - if (config?.instantiate) - this.instantiatePersistent(key).then((success) => { - this.state().isPersisted = success; - }); + this.instantiatePersistent({ + key: config.key, + storageKeys: config.storageKeys, + }); + + // Load/Store persisted Value for the first Time + if (this.ready && config.instantiate) this.initialLoading(); } //========================================================================================================= // Set Key //========================================================================================================= /** - * @public - * Sets Key/Name of Persistent + * @internal + * Updates Key/Name of Persistent * @param value - New Key/Name of Persistent */ - public async setKey(value: StorageKey) { - // If persistent isn't ready try to init it with the new Key - if (!this.ready) { - this.instantiatePersistent(value).then((success) => { - this.state().isPersisted = success; - }); - return; - } + public async setKey(value?: StorageKey): Promise { + const oldKey = this._key; + const wasReady = this.ready; - // Check if key has changed + // Assign Key if (value === this._key) return; + this._key = value || Persistent.placeHolderKey; - // Remove value with old Key - await this.removeValue(); + const isValid = this.validatePersistent(); - // Update Key - this._key = value; + // Try to Initial Load Value if persistent wasn't ready and return + if (!wasReady) { + if (isValid) await this.initialLoading(); + return; + } + + // Remove value at old Key + await this.removePersistedValue(oldKey); - // Set value with new Key - await this.updateValue(); + // Assign Value to new Key + if (isValid) await this.persistValue(value); } //========================================================================================================= - // Load Value + // Initial Loading //========================================================================================================= /** * @internal - * Loads Value from Storage + * Loads/Saves Storage Value for the first Time + */ + public async initialLoading() { + super.initialLoading().then(() => { + this.state().isPersisted = true; + }); + } + + //========================================================================================================= + // Load Persisted Value + //========================================================================================================= + /** + * @internal + * Loads State Value from the Storage * @return Success? */ - public async loadValue(): Promise { + public async loadPersistedValue(key?: PersistentKey): Promise { if (!this.ready) return false; - const loadedValue = await this.agileInstance().storages.get( - this._key, - this.storageKeys && this.storageKeys[0] + const _key = key || this._key; + + // Load Value from default Storage + const loadedValue = await this.agileInstance().storages.get( + _key, + this.defaultStorageKey ); - if (loadedValue) { - this.state().set(loadedValue, { storage: false }); - return true; - } - return false; + if (!loadedValue) return false; + + // Assign loaded Value to State + this.state().set(loadedValue, { storage: false }); + + // Persist State, so that the Storage Value updates dynamically if the State updates + await this.persistValue(_key); + + return true; } //========================================================================================================= - // Set Value + // Persist Value //========================================================================================================= /** * @internal - * Saves/Updates Value in Storage + * Sets everything up so that the State gets saved in the Storage on every Value change * @return Success? */ - public async updateValue(): Promise { + public async persistValue(key?: PersistentKey): Promise { if (!this.ready) return false; - this.agileInstance().storages.set( - this.key, - this.state().getPersistableValue(), - this.storageKeys + const _key = key || this._key; + + // Add SideEffect to State, that updates the saved State Value depending on the current State Value + this.state().addSideEffect( + StatePersistent.storeValueSideEffectKey, + (config) => { + this.rebuildStorageSideEffect(this.state(), _key, config); + } ); + + // Rebuild Storage for saving State Value in the Storage + this.rebuildStorageSideEffect(this.state(), _key); + this.isPersisted = true; return true; } //========================================================================================================= - // Remove Value + // Remove Persisted Value //========================================================================================================= /** * @internal - * Removes Value form Storage + * Removes State Value form the Storage * @return Success? */ - public async removeValue(): Promise { + public async removePersistedValue(key?: PersistentKey): Promise { if (!this.ready) return false; - this.agileInstance().storages.remove(this.key, this.storageKeys); + const _key = key || this._key; + + // Remove SideEffect + this.state().removeSideEffect(StatePersistent.storeValueSideEffectKey); + + // Remove Value from Storage + this.agileInstance().storages.remove(_key, this.storageKeys); + this.isPersisted = false; return true; } //========================================================================================================= - // Validate Key + // Format Key //========================================================================================================= /** * @internal - * Validates Storage Key - * @param key - Key that gets validated + * Formats Storage Key + * @param key - Key that gets formatted */ - public validateKey(key?: StorageKey): StorageKey | null { + public formatKey(key?: PersistentKey): PersistentKey | undefined { const state = this.state(); // Get key from State - if (!key && state.key) return state.key; + if (!key && state._key) return state._key; - // Return null if no key found - if (!key) return null; + if (!key) return; // Set State Key to Storage Key if State has no key - if (!state.key) state.key = key; + if (!state._key) state._key = key; return key; } -} -/** - * @param instantiate - If Persistent gets instantiated - * @param storageKeys - Key/Name of Storages which gets used to persist the State Value (NOTE: If not passed the default Storage will be used) - */ -export interface StatePersistentConfigInterface { - instantiate?: boolean; - storageKeys?: StorageKey[]; + //========================================================================================================= + // Rebuild Storage SideEffect + //========================================================================================================= + /** + * @internal + * Rebuilds Storage depending on the State Value (Saves current State Value into the Storage) + * @param state - State that holds the new Value + * @param key - Key/Name of Persistent + * @param config - Config + */ + public rebuildStorageSideEffect( + state: State, + key: PersistentKey, + config: any = {} + ) { + if (config.storage !== undefined && !config.storage) return; + + this.agileInstance().storages.set( + key, + this.state().getPersistableValue(), + this.storageKeys + ); + } } diff --git a/packages/core/src/state/state.runtime.job.ts b/packages/core/src/state/state.runtime.job.ts new file mode 100644 index 00000000..0cf1e7bb --- /dev/null +++ b/packages/core/src/state/state.runtime.job.ts @@ -0,0 +1,51 @@ +import { + defineConfig, + RuntimeJob, + RuntimeJobConfigInterface, + RuntimeJobKey, + StateObserver, +} from "../internal"; + +export class StateRuntimeJob extends RuntimeJob { + public config: StateRuntimeJobConfigInterface; + + constructor( + observer: StateObserver, + config: CreateStateRuntimeJobConfigInterface = {} + ) { + super(observer, config); + config = defineConfig(config, { + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + + this.config = { + background: config.background, + force: config.force, + sideEffects: config.sideEffects, + storage: config.storage, + overwrite: config.overwrite, + }; + } +} + +/** + * @param key - Key/Name of Job + */ +export interface CreateStateRuntimeJobConfigInterface + extends StateRuntimeJobConfigInterface { + key?: RuntimeJobKey; +} + +/** + * @param overwrite - If whole State Value gets overwritten with Job Value + * @param storage - If Job Value can be saved in Storage + */ +export interface StateRuntimeJobConfigInterface + extends RuntimeJobConfigInterface { + overwrite?: boolean; + storage?: boolean; +} diff --git a/packages/core/src/storages/index.ts b/packages/core/src/storages/index.ts index 81fc1a00..d85750b5 100644 --- a/packages/core/src/storages/index.ts +++ b/packages/core/src/storages/index.ts @@ -22,10 +22,10 @@ export class Storages { * @param config - Config */ constructor(agileInstance: Agile, config: StoragesConfigInterface = {}) { + this.agileInstance = () => agileInstance; config = defineConfig(config, { - localStorage: true, + localStorage: false, }); - this.agileInstance = () => agileInstance; if (config.localStorage) this.instantiateLocalStorage(); } @@ -36,13 +36,13 @@ export class Storages { * @internal * Instantiates Local Storage */ - private instantiateLocalStorage() { + public instantiateLocalStorage(): boolean { // Check if Local Storage is Available if (!Storages.localStorageAvailable()) { Agile.logger.warn( "Local Storage is here not available, to use Storage functionalities like persist please provide a custom Storage!" ); - return; + return false; } // Create and register Local Storage @@ -55,7 +55,7 @@ export class Storages { remove: localStorage.removeItem.bind(localStorage), }, }); - this.register(_localStorage, { default: true }); + return this.register(_localStorage, { default: true }); } //========================================================================================================= @@ -71,32 +71,40 @@ export class Storages { storage: Storage, config: RegisterConfigInterface = {} ): boolean { - const hasRegisteredStorage = notEqual(this.storages, {}); + const hasRegisteredAnyStorage = notEqual(this.storages, {}); // Check if Storage already exists if (this.storages.hasOwnProperty(storage.key)) { Agile.logger.error( - `Storage with the key/name ${storage.key} already exists` + `Storage with the key/name '${storage.key}' already exists` ); return false; } - // Set first added Storage to default (if it isn't set) - if (!hasRegisteredStorage && config.default === undefined) - config.default = true; + // Set first added Storage as default Storage + if (!hasRegisteredAnyStorage && config.default === false) { + Agile.logger.warn( + "Be aware that Agile has to assign the first added Storage as default Storage!" + ); + } + if (!hasRegisteredAnyStorage) config.default = true; // Register Storage this.storages[storage.key] = storage; - storage.ready = true; if (config.default) this.defaultStorage = storage; - // Transfer already saved Items into new Storage this.persistentInstances.forEach((persistent) => { - if ( - persistent.storageKeys && - persistent.storageKeys.includes(storage.key) - ) - persistent.initialLoading(persistent.key); + // If Persistent isn't ready and has no default StorageKey.. reassignStorageKeys and try to load it + if (!persistent.defaultStorageKey) { + persistent.assignStorageKeys(); + const isValid = persistent.validatePersistent(); + if (isValid) persistent.initialLoading(); + return; + } + + // Add Value of Persistent to newly registered Storage + if (persistent.storageKeys.includes(storage.key)) + persistent.persistValue(); }); return true; @@ -140,9 +148,22 @@ export class Storages { public get( key: StorageItemKey, storageKey?: StorageKey - ): GetType | Promise | undefined { - if (storageKey) return this.getStorage(storageKey)?.get(key); - return this.defaultStorage?.get(key); + ): Promise { + if (!this.hasStorage()) { + Agile.logger.error( + "No Storage found! Please provide at least one Storage." + ); + return Promise.resolve(undefined); + } + + // Call get Method in specific Storage + if (storageKey) { + const storage = this.getStorage(storageKey); + if (storage) return storage.get(key); + } + + // Call get Method in default Storage + return this.defaultStorage?.get(key) || Promise.resolve(undefined); } //========================================================================================================= @@ -152,7 +173,7 @@ export class Storages { * @internal * Saves/Updates value at provided Key * @param key - Key of Storage property - * @param value - new Value that gets set + * @param value - new Value that gets set at provided Key * @param storageKeys - Key/Name of Storages where the Value gets set (if not provided default Storage will be used) */ public set( @@ -160,11 +181,21 @@ export class Storages { value: any, storageKeys?: StorageKey[] ): void { + if (!this.hasStorage()) { + Agile.logger.error( + "No Storage found! Please provide at least one Storage." + ); + return; + } + + // Call set Method in specific Storages if (storageKeys) { for (let storageKey of storageKeys) this.getStorage(storageKey)?.set(key, value); return; } + + // Call set Method in default Storage this.defaultStorage?.set(key, value); } @@ -178,14 +209,35 @@ export class Storages { * @param storageKeys - Key/Name of Storages where the Value gets removed (if not provided default Storage will be used) */ public remove(key: StorageItemKey, storageKeys?: StorageKey[]): void { + if (!this.hasStorage()) { + Agile.logger.error( + "No Storage found! Please provide at least one Storage." + ); + return; + } + + // Call remove Method in specific Storages if (storageKeys) { for (let storageKey of storageKeys) this.getStorage(storageKey)?.remove(key); return; } + + // Call remove Method in default Storage this.defaultStorage?.remove(key); } + //========================================================================================================= + // Has Storage + //========================================================================================================= + /** + * @internal + * Check if at least one Storage got registered + */ + public hasStorage(): boolean { + return notEqual(this.storages, {}); + } + //========================================================================================================= // Local Storage Available //========================================================================================================= diff --git a/packages/core/src/storages/persistent.ts b/packages/core/src/storages/persistent.ts index 771f2e13..af5d3f62 100644 --- a/packages/core/src/storages/persistent.ts +++ b/packages/core/src/storages/persistent.ts @@ -1,23 +1,42 @@ -import { Agile, StorageKey } from "../internal"; +import { Agile, defineConfig, StorageKey } from "../internal"; export class Persistent { public agileInstance: () => Agile; - public _key: StorageKey = "unknown"; - public ready: boolean = false; - public isPersisted: boolean = false; // If Value is stored in Agile Storage + public static placeHolderKey = "__THIS_IS_A_PLACEHOLDER__"; + + public _key: PersistentKey; + public ready = false; + public isPersisted = false; // If Value is stored in Agile Storage public onLoad: ((success: boolean) => void) | undefined; // Gets called if PersistValue got loaded for the first Time - public storageKeys?: StorageKey[]; // StorageKeys in which the Persist Value gets saved + + // StorageKeys of Storages in that the Persisted Value gets saved + public storageKeys: StorageKey[] = []; + public defaultStorageKey: StorageKey | undefined; /** * @internal * Persistent - Handles storing of Agile Instances + * Note: No stand alone class!! * @param agileInstance - An instance of Agile - * @param storageKeys - Key/Name of Storages in which the Persist Value gets saved + * @param config - Config */ - constructor(agileInstance: Agile, storageKeys?: StorageKey[]) { + constructor( + agileInstance: Agile, + config: CreatePersistentConfigInterface = {} + ) { this.agileInstance = () => agileInstance; - this.storageKeys = storageKeys; + this._key = Persistent.placeHolderKey; + config = defineConfig(config, { + instantiate: true, + storageKeys: [], + }); + this.agileInstance().storages.persistentInstances.add(this); + if (config.instantiate) + this.instantiatePersistent({ + storageKeys: config.storageKeys, + key: config.key, + }); } /** @@ -49,30 +68,75 @@ export class Persistent { } //========================================================================================================= - // Init Persistent + // Instantiate Persistent + //========================================================================================================= + /** + * @internal + * Instantiates this Class + * Note: Had to outsource it from the constructor because some extending classes + * have to define some stuff before being able to instantiate the parent (this) + * @param config - Config + */ + public instantiatePersistent(config: PersistentConfigInterface = {}) { + this._key = this.formatKey(config.key) || Persistent.placeHolderKey; + this.assignStorageKeys(config.storageKeys); + this.validatePersistent(); + } + + //========================================================================================================= + // Validate Persistent //========================================================================================================= /** * @internal - * Inits Persistent (this class) - * -> Sometimes this class needs to be instantiated after some properties have been set in extended class - * @param key - Key of Storage property + * Validates Persistent and updates its 'ready' property */ - public async instantiatePersistent(key?: StorageKey): Promise { + public validatePersistent(): boolean { + let isValid = true; + // Validate Key - const finalKey = this.validateKey(key); - if (!finalKey) { - Agile.logger.error("No persist Key found!"); - return false; + if (this._key === Persistent.placeHolderKey) { + Agile.logger.error( + "No valid persist Key found! Please provide a Key or assign one to the parent instance." + ); + isValid = false; } - this._key = finalKey; - this.agileInstance().storages.persistentInstances.add(this); - this.ready = true; + // Validate StorageKeys + if (!this.defaultStorageKey || this.storageKeys.length <= 0) { + Agile.logger.error( + "No persist Storage Key found! Please provide at least one Storage Key." + ); + isValid = false; + } - // Load/Store persisted Value/s for the first Time - await this.initialLoading(finalKey); + this.ready = isValid; + return isValid; + } - return true; + //========================================================================================================= + // Assign StorageKeys + //========================================================================================================= + /** + * @internal + * Assign new StorageKeys to Persistent and overwrite the old ones + * @param storageKeys - New Storage Keys + */ + public assignStorageKeys(storageKeys: StorageKey[] = []) { + const storages = this.agileInstance().storages; + + // Set default Agile Storage to defaultStorage if no storageKey provided + if (storageKeys.length <= 0) { + this.storageKeys = []; + if (storages.defaultStorage) { + const key = storages.defaultStorage.key; + this.defaultStorageKey = key; + this.storageKeys.push(key); + } + return; + } + + this.storageKeys = storageKeys; + this.defaultStorageKey = storageKeys[0]; } //========================================================================================================= @@ -81,12 +145,11 @@ export class Persistent { /** * @internal * Loads/Saves Storage Value for the first Time - * @param key - Key of Storage property */ - public async initialLoading(key: StorageKey) { - const success = await this.loadValue(); + public async initialLoading() { + const success = await this.loadPersistedValue(); if (this.onLoad) this.onLoad(success); - if (!success) await this.updateValue(); + if (!success) await this.persistValue(); } //========================================================================================================= @@ -97,9 +160,9 @@ export class Persistent { * Loads Value from Storage * @return Success? */ - public async loadValue(): Promise { + public async loadPersistedValue(key?: PersistentKey): Promise { Agile.logger.error( - `Didn't set loadValue function in Persistent '${this.key}'` + `'loadPersistedValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!` ); return false; } @@ -112,9 +175,9 @@ export class Persistent { * Saves/Updates Value in Storage * @return Success? */ - public async updateValue(): Promise { + public async persistValue(key?: PersistentKey): Promise { Agile.logger.error( - `Didn't set setValue function in Persistent '${this.key}'` + `'persistValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!` ); return false; } @@ -127,25 +190,44 @@ export class Persistent { * Removes Value form Storage * @return Success? */ - public async removeValue(): Promise { + public async removePersistedValue(key?: PersistentKey): Promise { Agile.logger.error( - `Didn't set removeValue function in Persistent '${this.key}'` + `'removePersistedValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!` ); return false; } //========================================================================================================= - // Validate Key + // Format Key //========================================================================================================= /** * @internal * Validates Storage Key * @param key - Key that gets validated */ - public validateKey(key?: StorageKey): StorageKey | null { - Agile.logger.error( - `Didn't set validateKey function in Persistent '${this.key}'` - ); - return null; + public formatKey(key?: PersistentKey): PersistentKey | undefined { + return key; } } + +export type PersistentKey = string | number; + +/** + * @param key - Key/Name of Persistent + * @param storageKeys - Keys of Storages in that the persisted Value gets saved + * @param instantiate - If Persistent gets Instantiated immediately + */ +export interface CreatePersistentConfigInterface { + key?: PersistentKey; + storageKeys?: StorageKey[]; + instantiate?: boolean; +} + +/** + * @param key - Key/Name of Persistent + * @param storageKeys - Keys of Storages in that the persisted Value gets saved + */ +export interface PersistentConfigInterface { + key?: PersistentKey; + storageKeys?: StorageKey[]; +} diff --git a/packages/core/src/storages/storage.ts b/packages/core/src/storages/storage.ts index 66b3bdbc..95ff4bb4 100644 --- a/packages/core/src/storages/storage.ts +++ b/packages/core/src/storages/storage.ts @@ -8,7 +8,7 @@ import { export class Storage { public key: StorageKey; - public ready: boolean = false; + public ready = false; public methods: StorageMethodsInterface; public config: StorageConfigInterface; @@ -29,6 +29,15 @@ export class Storage { async: config.async, }; this.ready = this.validate(); + if (!this.ready) return; + + // Check if Storage is async + if ( + isAsyncFunction(this.methods.get) || + isAsyncFunction(this.methods.set) || + isAsyncFunction(this.methods.remove) + ) + this.config.async = true; } //========================================================================================================= @@ -36,10 +45,9 @@ export class Storage { //========================================================================================================= /** * @public - * Validates this Storage + * Validates Storage Methods */ public validate(): boolean { - // Validate Functions if (!isFunction(this.methods?.get)) { Agile.logger.error("Your GET StorageMethod isn't valid!"); return false; @@ -52,35 +60,26 @@ export class Storage { Agile.logger.error("Your REMOVE StorageMethod isn't valid!"); return false; } - - // Check if Storage is async - if ( - isAsyncFunction(this.methods.get) || - isAsyncFunction(this.methods.set) || - isAsyncFunction(this.methods.remove) - ) - this.config.async = true; - return true; } //========================================================================================================= - // Get + // Normal Get //========================================================================================================= /** - * @public - * Gets value at provided Key + * @internal + * Gets value at provided Key (normal) + * Note: Only use this if you are 100% sure this Storage doesn't work async * @param key - Key of Storage property */ - public get( - key: StorageItemKey - ): GetType | Promise | undefined { + public normalGet(key: StorageItemKey): GetTpe | undefined { if (!this.ready || !this.methods.get) return; + if (isAsyncFunction(this.methods.get)) + Agile.logger.warn( + "Be aware that 'normalGet' returns a Promise with a stringified Value if using it in an async Storage!" + ); - // Async Get - if (this.config.async) return this.asyncGet(key); - - // Normal Get + // Get Value const res = this.methods.get(this.getStorageKey(key)); if (isJsonString(res)) return JSON.parse(res); return res; @@ -94,12 +93,19 @@ export class Storage { * Gets value at provided Key (async) * @param key - Key of Storage property */ - private asyncGet(key: StorageItemKey): Promise { + public get(key: StorageItemKey): Promise { + if (!this.ready || !this.methods.get) return Promise.resolve(undefined); + + // Get Value in 'dummy' promise if get method isn't async + if (!isAsyncFunction(this.methods.get)) + return Promise.resolve(this.normalGet(key)); + + // Get Value (async) return new Promise((resolve, reject) => { this.methods ?.get(this.getStorageKey(key)) .then((res: any) => { - if (isJsonString(res)) return resolve(JSON.parse(res)); + if (isJsonString(res)) resolve(JSON.parse(res)); resolve(res); }) .catch(reject); @@ -141,8 +147,10 @@ export class Storage { * Creates Storage Key from provided key * @param key - Key that gets converted into a Storage Key */ - private getStorageKey(key: StorageItemKey): string { - return `_${this.config.prefix}_${key}`; + public getStorageKey(key: StorageItemKey): string { + return this.config.prefix + ? `_${this.config.prefix}_${key}` + : key.toString(); } } diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index c746d401..40a2e2d3 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -93,27 +93,26 @@ export function normalizeArray( //========================================================================================================= /** * @internal - * Tries to get an AgileInstance from provided instance - * If no agileInstance found it returns the global AgileInstance - * @param instance - Instance that might hold an AgileInstance + * Tries to get an Instance of Agile from provided Instance + * If no agileInstance found it returns the global bound Agile Instance + * @param instance - Instance that might hold an Agile Instance */ export function getAgileInstance(instance: any): Agile | undefined { try { - // Try to find agileInstance in Instance + // Try to get agileInstance from passed Instance if (instance) { - if (instance instanceof State) return instance.agileInstance(); - if (instance instanceof Event) return instance.agileInstance(); - if (instance instanceof Collection) return instance.agileInstance(); - if (instance instanceof Observer) return instance.agileInstance(); - const _agileInstance = instance["agileInstance"]; - if (_agileInstance) return instance; + const _agileInstance = isFunction(instance["agileInstance"]) + ? instance["agileInstance"]() + : instance["agileInstance"]; + if (_agileInstance) return _agileInstance; } - // Return global bound agileInstance (set in first instantiation of Agile) + // Return global bound agileInstance return globalThis["__agile__"]; } catch (e) { - Agile.logger.error("Failed to get Agile Instance", e); + Agile.logger.error("Failed to get Agile Instance from ", instance); } + return undefined; } @@ -138,7 +137,12 @@ export function isFunction(value: any): boolean { * @param value - Value that gets tested if its an async function */ export function isAsyncFunction(value: any): boolean { - return isFunction(value) && value.constructor.name === "AsyncFunction"; + const valueString = value.toString(); + return ( + isFunction(value) && + (value.constructor.name === "AsyncFunction" || + valueString.includes("__awaiter")) + ); } //========================================================================================================= @@ -172,6 +176,7 @@ export function isValidUrl(url: string): boolean { * @param value - Value that gets checked */ export function isJsonString(value: any): boolean { + if (typeof value !== "string") return false; try { JSON.parse(value); } catch (e) { @@ -188,11 +193,23 @@ export function isJsonString(value: any): boolean { * Merges default values/properties into config object * @param config - Config object that receives default values * @param defaults - Default values object that gets merged into config object + * @param overwriteUndefinedProperties - If undefined Properties in config gets overwritten by the default value */ export function defineConfig( config: ConfigInterface, - defaults: Object + defaults: Object, + overwriteUndefinedProperties?: boolean ): ConfigInterface { + if (overwriteUndefinedProperties === undefined) + overwriteUndefinedProperties = true; + + if (overwriteUndefinedProperties) { + const finalConfig = { ...defaults, ...config }; + for (let key in finalConfig) + if (finalConfig[key] === undefined) finalConfig[key] = defaults[key]; + return finalConfig; + } + return { ...defaults, ...config }; } @@ -267,7 +284,7 @@ export function notEqual(value1: any, value2: any): boolean { * Generates random Id * @param length - Length of generated Id */ -export function generateId(length?: number) { +export function generateId(length?: number): string { const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charactersLength = characters.length; @@ -288,8 +305,14 @@ export function generateId(length?: number) { * @param instance - Instance of Class you want to clone */ export function clone(instance: T): T { - const copy: T = Object.create(Object.getPrototypeOf(instance)); - return Object.assign(copy, instance); + // Clone Class + const objectCopy: T = Object.create(Object.getPrototypeOf(instance)); + const objectClone = Object.assign(objectCopy, instance); + + // Copy Properties of Class + for (let key in objectClone) objectClone[key] = copy(objectClone[key]); + + return objectClone; } //========================================================================================================= @@ -297,19 +320,30 @@ export function clone(instance: T): T { //========================================================================================================= /** * @internal - * Binds Instance Global + * Binds passed Instance globally at passed Key * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis * https://blog.logrocket.com/what-is-globalthis-why-use-it/ - * @param key - Key of Instance - * @param instance - Instance which becomes globally accessible (globalThis.key) + * @param key - Key/Name of Instance + * @param instance - Instance + * @param overwrite - If already existing instance at passed Key gets overwritten */ -export function globalBind(key: string, instance: any) { +export function globalBind( + key: string, + instance: any, + overwrite = false +): boolean { try { - if (!globalThis[key]) globalThis[key] = instance; + if (overwrite) { + globalThis[key] = instance; + return true; + } + + if (!globalThis[key]) { + globalThis[key] = instance; + return true; + } } catch (e) { - Agile.logger.error( - `Failed to create global Instance called '${name}'`, - instance - ); + Agile.logger.error(`Failed to create global Instance called '${key}'`); } + return false; } diff --git a/packages/core/tests/collection/default.spec.ts b/packages/core/tests/collection/default.spec.ts deleted file mode 100644 index 427c4fbe..00000000 --- a/packages/core/tests/collection/default.spec.ts +++ /dev/null @@ -1,281 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group, Selector } from "../../src"; - -describe("Default Collection Tests", () => { - // Define Agile - const App = new Agile(); - - interface userInterface { - id: number; - name: string; - } - - describe("Collection", () => { - // Set Collections - const MY_COLLECTION = App.Collection(); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.config.primaryKey).to.eq( - "id", - "MY_COLLECTION has correct config.primaryKey" - ); - expect(MY_COLLECTION.config.defaultGroupKey).to.eq( - "default", - "MY_COLLECTION has correct config.defaultGroupKey" - ); - - expect(MY_COLLECTION.key).to.eq( - undefined, - "MY_COLLECTION has correct key" - ); - expect(MY_COLLECTION._key).to.eq( - undefined, - "MY_COLLECTION has correct _key" - ); - expect(JSON.stringify(MY_COLLECTION.data)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has no data" - ); - expect(MY_COLLECTION.isPersisted).to.eq( - false, - "MY_COLLECTION is no persisted Collection" - ); - expect( - Object.keys(MY_COLLECTION.groups).length === 1 && - MY_COLLECTION.groups["default"] !== undefined - ).to.eq(true, "MY_COLLECTION has only default group"); - expect(JSON.stringify(MY_COLLECTION.selectors)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has no selectors" - ); - expect(MY_COLLECTION.size).to.eq(0, "MY_COLLECTION has correct size"); - }); - - it("Can change key", () => { - // Update key - MY_COLLECTION.key = "myKey"; - - expect(MY_COLLECTION.key).to.eq("myKey", "MY_COLLECTION has correct key"); - expect(MY_COLLECTION._key).to.eq( - "myKey", - "MY_COLLECTION has correct _key" - ); - }); - }); - - describe("Collection with Key", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - key: "myCollectionKey", - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.key).to.eq( - "myCollectionKey", - "MY_COLLECTION has correct key" - ); - expect(MY_COLLECTION._key).to.eq( - "myCollectionKey", - "MY_COLLECTION has correct _key" - ); - }); - - it("Can change key", () => { - // Update key - MY_COLLECTION.key = "withNewKey"; - - expect(MY_COLLECTION.key).to.eq( - "withNewKey", - "MY_COLLECTION has correct key" - ); - expect(MY_COLLECTION._key).to.eq( - "withNewKey", - "MY_COLLECTION has correct _key" - ); - }); - }); - - describe("Collection with default Groups", () => { - describe("Default Groups in Array shape", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - groups: ["group1", "group2"], - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION has group1 in groups" - ); - expect(MY_COLLECTION.groups["group2"] instanceof Group).to.eq( - true, - "MY_COLLECTION has group2 in groups" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "group1", - "group1 has correct key" - ); - expect(MY_COLLECTION.groups["group2"].key).to.eq( - "group2", - "group2 has correct key" - ); - }); - }); - - describe("Default Groups in Object shape", () => { - interface userInterface { - id: number; - name: string; - } - - // Set Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group(), - group2: collection.Group(), - }, - })); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION has group1 in groups" - ); - expect(MY_COLLECTION.groups["group2"] instanceof Group).to.eq( - true, - "MY_COLLECTION has group2 in groups" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "group1", - "group1 has correct key" - ); - expect(MY_COLLECTION.groups["group2"].key).to.eq( - "group2", - "group2 has correct key" - ); - }); - }); - }); - - describe("Collection with Key", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - key: "myCollectionKey", - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.key).to.eq( - "myCollectionKey", - "MY_COLLECTION has correct key" - ); - expect(MY_COLLECTION._key).to.eq( - "myCollectionKey", - "MY_COLLECTION has correct _key" - ); - }); - - it("Can change key", () => { - // Update key - MY_COLLECTION.key = "withNewKey"; - - expect(MY_COLLECTION.key).to.eq( - "withNewKey", - "MY_COLLECTION has correct key" - ); - expect(MY_COLLECTION._key).to.eq( - "withNewKey", - "MY_COLLECTION has correct _key" - ); - }); - }); - - describe("Collection with default Selectors", () => { - describe("Default Selector in Array shape", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - selectors: ["selector1", "selector2"], - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION has selector1 in selectors" - ); - expect(MY_COLLECTION.selectors["selector2"] instanceof Selector).to.eq( - true, - "MY_COLLECTION has selector2 in selectors" - ); - expect(MY_COLLECTION.selectors["selector1"].key).to.eq( - "selector1", - "selector1 has correct key" - ); - expect(MY_COLLECTION.selectors["selector2"].key).to.eq( - "selector2", - "selector2 has correct key" - ); - }); - }); - - describe("Default Selectors in Object shape", () => { - // Set Collection - const MY_COLLECTION = App.Collection((collection) => ({ - selectors: { - selector1: collection.Selector("3"), - selector2: collection.Selector("2"), - }, - })); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION has selector1 in selectors" - ); - expect(MY_COLLECTION.selectors["selector2"] instanceof Selector).to.eq( - true, - "MY_COLLECTION has selector2 in selectors" - ); - expect(MY_COLLECTION.selectors["selector1"].key).to.eq( - "selector1", - "selector1 has correct key" - ); - expect(MY_COLLECTION.selectors["selector2"].key).to.eq( - "selector2", - "selector2 has correct key" - ); - }); - }); - }); - - describe("Collection with primaryKey", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - primaryKey: "key", - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.config.primaryKey).to.eq( - "key", - "MY_COLLECTION has correct primaryKey" - ); - }); - }); - - describe("Collection with defaultGroupKey", () => { - // Set Collection - const MY_COLLECTION = App.Collection({ - defaultGroupKey: "normal", - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.config.defaultGroupKey).to.eq( - "normal", - "MY_COLLECTION has correct defaultGroupKey" - ); - expect(MY_COLLECTION.groups["normal"] instanceof Group).to.eq( - true, - "MY_COLLECTION has default group in groups" - ); - }); - }); -}); diff --git a/packages/core/tests/collection/functions/collect.function.spec.ts b/packages/core/tests/collection/functions/collect.function.spec.ts deleted file mode 100644 index 7dbe5ea9..00000000 --- a/packages/core/tests/collection/functions/collect.function.spec.ts +++ /dev/null @@ -1,571 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Group, Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Collect Function Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - describe("Collection without primaryKey", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - age?: number; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("default").observer]); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has correct data" - ); - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["default"]?.observer.subs.size === 1).to.eq( - true, - "MY_COLLECTION default Group has correct subs size" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount is 0"); - }); - - it("Can collect item", async () => { - // Collect item - MY_COLLECTION.collect({ id: 1, name: "jeff" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[1]?.value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 1 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.size).to.eq( - 1, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - - it("Can't collect item with no primaryKey", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ name: "benno" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.size).to.eq(1, "MY_COLLECTION size stayed the same"); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - - it("Can't collect no object item", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect("franz"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.size).to.eq(1, "MY_COLLECTION size stayed the same"); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - - it("Can't collect item with wrong primaryKey", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ key: 2, name: "hans" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify(undefined), - "MY_COLLECTION data hasn't collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 2 - ) !== -1 - ).to.eq( - false, - "MY_COLLECTION default group doesn't contain collected value" - ); - expect(MY_COLLECTION.size).to.eq(1, "MY_COLLECTION size stayed the same"); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - - it("Can collect multiple items at the same time", async () => { - // Collect item - MY_COLLECTION.collect([ - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION data has collected value with id 2" - ); - expect(JSON.stringify(MY_COLLECTION.data[3]?.value)).to.eq( - JSON.stringify({ - id: 3, - name: "frank", - }), - "MY_COLLECTION data has collected value with id 3" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 2 - ) !== -1 - ).to.eq( - true, - "MY_COLLECTION default group contains collected value with id 2" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 3 - ) !== -1 - ).to.eq( - true, - "MY_COLLECTION default group contains collected value with id 3" - ); - expect(MY_COLLECTION.size).to.eq( - 3, - "MY_COLLECTION size has been increased by 2" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can overwrite item if collecting with existing primaryKey", async () => { - MY_COLLECTION.collect({ id: 2, name: "benno" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - name: "benno", - }), - "MY_COLLECTION data has overwritten value with id 2" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION size stayed the same"); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 2 - ) !== -1 - ).to.eq( - true, - "MY_COLLECTION default group still contains collected value" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by collecting Item with background = false", async () => { - // Collect item - MY_COLLECTION.collect({ id: 4, name: "dustin" }, [], { - background: false, - }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[4]?.value)).to.eq( - JSON.stringify({ - id: 4, - name: "dustin", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 4 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.size).to.eq( - 4, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by collecting Item with background = true", async () => { - // Collect item - MY_COLLECTION.collect({ id: 5, name: "snipey" }, [], { - background: true, - }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[5]?.value)).to.eq( - JSON.stringify({ - id: 5, - name: "snipey", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 5 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.size).to.eq( - 5, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount stayed the same"); - }); - }); - - describe("Test method option", () => { - it("Does add the item at the end of the group with method = 'push'", async () => { - // Collect item - MY_COLLECTION.collect({ id: 6, name: "jana" }, [], { method: "push" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[6]?.value)).to.eq( - JSON.stringify({ - id: 6, - name: "jana", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 6 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect( - MY_COLLECTION.groups["default"]?.value[ - MY_COLLECTION.groups["default"]?.value.length - 1 - ] - ).to.eq( - 6, - "MY_COLLECTION default group collected value is last item in group" - ); - expect(MY_COLLECTION.size).to.eq( - 6, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(5, "rerenderCount has been increased by 1"); - }); - - it("Does add the item at the start of the group with method = 'unshift'", async () => { - // Collect item - MY_COLLECTION.collect({ id: 7, name: "gina" }, [], { - method: "unshift", - }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[7]?.value)).to.eq( - JSON.stringify({ - id: 7, - name: "gina", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 7 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.groups["default"]?.value[0]).to.eq( - 7, - "MY_COLLECTION default group collected value is first item in group" - ); - expect(MY_COLLECTION.size).to.eq( - 7, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(6, "rerenderCount has been increased by 1"); - }); - }); - - describe("Test patch option", () => { - it("Does patch value into item without overwriting it with patch = true", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ id: 1, age: 20 }, [], { patch: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[1]?.value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - age: 20, - }), - "MY_COLLECTION data at id 1 has changed" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 1 - ) !== -1 - ).to.eq( - true, - "MY_COLLECTION default group still contains collected value" - ); - expect(MY_COLLECTION.size).to.eq( - 7, - "MY_COLLECTION size stayed the same" - ); - - expect(rerenderCount).to.eq(7, "rerenderCount has been increased by 1"); - }); - - it("Does set value into item by overwriting it with patch = false", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ id: 2, age: 30 }, [], { patch: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - age: 30, - }), - "MY_COLLECTION data at id 2 has changed" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 2 - ) !== -1 - ).to.eq( - true, - "MY_COLLECTION default group still contains collected value" - ); - expect(MY_COLLECTION.size).to.eq( - 7, - "MY_COLLECTION size stayed the same" - ); - - expect(rerenderCount).to.eq(8, "rerenderCount has been increased by 1"); - }); - }); - - describe("Test forEachItem option", () => { - it("Does call forEachItem", async () => { - const myForEachItems: { - item: userInterface; - key: string | number; - index: number; - }[] = []; - - // Collect item - MY_COLLECTION.collect( - [ - { id: 8, name: "joshi" }, - { id: 9, name: "paul" }, - ], - [], - { - forEachItem: (item, key, index) => { - myForEachItems.push({ - item: item, - key: key, - index: index, - }); - }, - } - ); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[8]?.value)).to.eq( - JSON.stringify({ - id: 8, - name: "joshi", - }), - "MY_COLLECTION data has collected value" - ); - expect(JSON.stringify(MY_COLLECTION.data[9]?.value)).to.eq( - JSON.stringify({ - id: 9, - name: "paul", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 8 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 9 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.size).to.eq( - 9, - "MY_COLLECTION size has been increased by 2" - ); - - expect(JSON.stringify(myForEachItems[0])).to.eq( - JSON.stringify({ - item: { id: 8, name: "joshi" }, - key: 8, - index: 0, - }), - "myForEachItem has contains first collected item" - ); - expect(JSON.stringify(myForEachItems[1])).to.eq( - JSON.stringify({ - item: { id: 9, name: "paul" }, - key: 9, - index: 1, - }), - "myForEachItem has contains second collected item" - ); - - expect(rerenderCount).to.eq(9, "rerenderCount has been increased by 1"); - }); - }); - }); - - describe("Collection with primaryKey and defaultGroupKey", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - key: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection({ - primaryKey: "key", - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("default")?.observer]); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has correct data" - ); - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["default"]?.observer.subs.size === 1).to.eq( - true, - "MY_COLLECTION default Group has correct subs size" - ); - }); - - it("Can collect item", async () => { - // Collect item - MY_COLLECTION.collect({ key: 1, name: "jeff" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[1]?.value)).to.eq( - JSON.stringify({ - key: 1, - name: "jeff", - }), - "MY_COLLECTION data has collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 1 - ) !== -1 - ).to.eq(true, "MY_COLLECTION default group contains collected value"); - expect(MY_COLLECTION.size).to.eq( - 1, - "MY_COLLECTION size has been increased by 1" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - - it("Can't collect item with no primaryKey", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ name: "benno" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.size).to.eq(1, "MY_COLLECTION size stayed the same"); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - - it("Can't collect item with wrong primaryKey", async () => { - // Collect item - // @ts-ignore - MY_COLLECTION.collect({ id: 2, name: "hans" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify(undefined), - "MY_COLLECTION data hasn't collected value" - ); - expect( - MY_COLLECTION.groups["default"]?.value.findIndex( - (value) => value === 2 - ) !== -1 - ).to.eq( - false, - "MY_COLLECTION default group doesn't contain collected value" - ); - expect(MY_COLLECTION.size).to.eq(1, "MY_COLLECTION size stayed the same"); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - }); -}); diff --git a/packages/core/tests/collection/functions/createGroup.function.spec.ts b/packages/core/tests/collection/functions/createGroup.function.spec.ts deleted file mode 100644 index 141eb3ea..00000000 --- a/packages/core/tests/collection/functions/createGroup.function.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../src"; - -describe("createGroup Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has correct data" - ); - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - }); - - it("Can create Group", () => { - // Create Group - MY_COLLECTION.createGroup("group1"); - - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 has been created" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 has no initialItems" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "group1", - "group1 has correct key" - ); - }); - - it("Can create Group with initial Items", () => { - // Create Group - MY_COLLECTION.createGroup("group2", ["1", "2"]); - - expect(MY_COLLECTION.groups["group2"] instanceof Group).to.eq( - true, - "MY_COLLECTION group2 has been created" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify(["1", "2"]), - "group2 has initialItems" - ); - expect(MY_COLLECTION.groups["group2"].key).to.eq( - "group2", - "group2 has correct key" - ); - }); - - it("Can't overwrite Group which already exists", () => { - // Create Group - MY_COLLECTION.createGroup("group2"); - - expect(MY_COLLECTION.groups["group2"] instanceof Group).to.eq( - true, - "MY_COLLECTION group2 is still a group" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify(["1", "2"]), - "group2 items stayed the same" - ); - }); -}); diff --git a/packages/core/tests/collection/functions/createSelector.function.spec.ts b/packages/core/tests/collection/functions/createSelector.function.spec.ts deleted file mode 100644 index a94a4869..00000000 --- a/packages/core/tests/collection/functions/createSelector.function.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Selector, Item } from "../../../src"; - -describe("createSelector Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.collect({ id: 1, name: "jeff" }); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION has correct data" - ); - expect(JSON.stringify(MY_COLLECTION.selectors)).to.eq( - JSON.stringify({}), - "MY_COLLECTION has no initial selectors" - ); - }); - - it("Can create Selector with not existing id", () => { - // Create Selector - MY_COLLECTION.createSelector("selector1", 3); - - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 has been created" - ); - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 3, - "selector1 is watching right id" - ); - expect(MY_COLLECTION.selectors["selector1"].key).to.eq( - "selector1", - "selector1 has correct key" - ); - - expect(MY_COLLECTION.data[3] instanceof Item).to.eq( - true, - "MY_COLLECTION data contains dummy Item with id 3 (created by Selector)" - ); - expect(MY_COLLECTION.data[3].isPlaceholder).to.eq( - true, - "Item at id 3 is placeholder" - ); - }); - - it("Can create Selector with existing id", () => { - // Create Selector - MY_COLLECTION.createSelector("selector2", 1); - - expect(MY_COLLECTION.selectors["selector2"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 has been created" - ); - expect(MY_COLLECTION.selectors["selector2"].itemKey).to.eq( - 1, - "selector1 is watching right id" - ); - expect(MY_COLLECTION.selectors["selector2"].key).to.eq( - "selector2", - "selector1 has correct key" - ); - }); - - it("Can't overwrite Selector which already exists", () => { - // Create Selector - MY_COLLECTION.createSelector("selector1", 2); - - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 is still a selector" - ); - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 3, - "selector1 id stayed the same (3)" - ); - }); -}); diff --git a/packages/core/tests/collection/functions/findById.function.spec.ts b/packages/core/tests/collection/functions/findById.function.spec.ts deleted file mode 100644 index 1ab6455e..00000000 --- a/packages/core/tests/collection/functions/findById.function.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { State, Agile } from "../../../src"; - -describe("FindById Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.collect({ id: 1, name: "jeff" }); - MY_COLLECTION.collect({ id: 2, name: "hans" }); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION has correct data" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION has correct data" - ); - expect(MY_COLLECTION.size).to.eq(2, "MY_COLLECTION has correct size"); - }); - - it("Can findById", () => { - const item = MY_COLLECTION.getItem(1); - - expect(item instanceof State).to.eq(true, "item is instanceof State"); - expect(JSON.stringify(item?.value)).to.eq( - JSON.stringify({ id: 1, name: "jeff" }), - "item has correct value" - ); - }); - - it("Can't findById not existing item", () => { - const item = MY_COLLECTION.getItem(5); - - expect(item).to.eq(undefined, "item is undefined"); - }); -}); diff --git a/packages/core/tests/collection/functions/getGroup.function.spec.ts b/packages/core/tests/collection/functions/getGroup.function.spec.ts deleted file mode 100644 index 4ac02fad..00000000 --- a/packages/core/tests/collection/functions/getGroup.function.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../src"; - -describe("getGroup Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection({ - groups: ["group1", "group2"], - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(MY_COLLECTION.groups["group2"] instanceof Group).to.eq( - true, - "MY_COLLECTION group2 Group has been created" - ); - }); - - it("Can get Group which exists", () => { - // Get Group - const myGroup = MY_COLLECTION.getGroup("group1"); - - expect(myGroup instanceof Group).to.eq(true, "myGroup is a group"); - expect(myGroup.key).to.eq("group1", "myGroup has correct key"); - expect(myGroup.exists).to.eq(true, "myGroup does exist"); - }); - - it("Can't get Group which doesn't exist", () => { - // Get Group - const myGroup = MY_COLLECTION.getGroup("group3"); - - expect(myGroup instanceof Group).to.eq(false, "myGroup is undefined"); - }); -}); diff --git a/packages/core/tests/collection/functions/getSelector.function.spec.ts b/packages/core/tests/collection/functions/getSelector.function.spec.ts deleted file mode 100644 index 673686ff..00000000 --- a/packages/core/tests/collection/functions/getSelector.function.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Selector } from "../../../src"; - -describe("getSelector Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection({ - selectors: ["selector1", "selector2"], - }); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 Selector has been created" - ); - expect(MY_COLLECTION.selectors["selector2"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector2 Selector has been created" - ); - }); - - it("Can get Selector which exists", () => { - // Get Selector - const mySelector = MY_COLLECTION.getSelector("selector1"); - - expect(mySelector instanceof Selector).to.eq( - true, - "mySelector is a Selector" - ); - expect(mySelector?.key).to.eq("selector1", "mySelector has correct key"); - expect(mySelector?.exists).to.eq( - false, - "mySelector doesn't exist because it has no value" - ); - expect(mySelector?.value).to.eq(undefined, "mySelector has correct value"); - }); - - it("Can't get Selector which doesn't exist", () => { - // Get Selector - const mySelector = MY_COLLECTION.getSelector("selector3"); - - expect(mySelector instanceof Selector).to.eq( - false, - "mySelector is undefined" - ); - }); -}); diff --git a/packages/core/tests/collection/functions/getValueById.function.spec.ts b/packages/core/tests/collection/functions/getValueById.function.spec.ts deleted file mode 100644 index 923047d6..00000000 --- a/packages/core/tests/collection/functions/getValueById.function.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("GetValueById Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.collect({ id: 1, name: "jeff" }); - MY_COLLECTION.collect({ id: 2, name: "hans" }); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION has correct data" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION has correct data" - ); - expect(MY_COLLECTION.size).to.eq(2, "MY_COLLECTION has correct size"); - }); - - it("Can getValueById", () => { - const item = MY_COLLECTION.getItemValue(1); - - expect(JSON.stringify(item)).to.eq( - JSON.stringify({ id: 1, name: "jeff" }), - "item has correct value" - ); - }); - - it("Can't getValueById with not existing item id", () => { - const item = MY_COLLECTION.getItem(5); - - expect(item).to.eq(undefined, "item is undefined"); - }); -}); diff --git a/packages/core/tests/collection/functions/persist.function.spec.ts b/packages/core/tests/collection/functions/persist.function.spec.ts deleted file mode 100644 index 1d73c2be..00000000 --- a/packages/core/tests/collection/functions/persist.function.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Persist Function Tests", () => { - const myStorage: any = {}; - - // Define Agile with Storage - const App = new Agile(); - App.registerStorage( - App.Storage({ - key: "testStorage", - prefix: "test", - methods: { - get: (key) => { - return myStorage[key]; - }, - set: (key, value) => { - console.log(`SET '${key}'`, value); - myStorage[key] = value; - }, - remove: (key) => { - console.log(`DELETE '${key}'`); - delete myStorage[key]; - }, - }, - }) - ); - - interface User { - id: number; - name: string; - } - - describe("Collection", () => { - it("Can persist Collection", async () => { - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.persist("myCollection"); - MY_COLLECTION.collect({ id: 2, name: "hans" }); - MY_COLLECTION.collect({ id: 1, name: "frank" }); - MY_COLLECTION.createGroup("stuipidPeople", [1, 2]).persist({ - followCollectionPattern: true, - }); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - MY_COLLECTION.collect({ id: 3, name: "günter" }); - - // Needs some time to collect value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - - MY_COLLECTION.update(3, { name: "Benno" }); - - // Needs some time to update value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - - MY_COLLECTION.update(1, { id: 37, name: "Arne" }); - - // Needs some time to update value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - expect(myStorage !== undefined).to.eq(true); - }); - - it("Can load persisted Collection", async () => { - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.persist("myCollection"); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - console.log(MY_COLLECTION); - - MY_COLLECTION.update(3, { name: "Angela" }); - MY_COLLECTION.collect({ id: 4, name: "Paul" }); - MY_COLLECTION.collect({ id: 99, name: "Jeff" }); - - // Needs some time to collect/update value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - - expect(myStorage !== undefined).to.eq(true); - }); - - it("Can remove persisted Collection", async () => { - // Create Collection - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.persist("myCollection"); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - console.log(MY_COLLECTION); - - MY_COLLECTION.persistent?.removeValue(); - - // Needs some time to remove value - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("MyStorage ", myStorage); - console.log(MY_COLLECTION); - }); - }); -}); diff --git a/packages/core/tests/collection/functions/put.function.spec.ts b/packages/core/tests/collection/functions/put.function.spec.ts deleted file mode 100644 index 06675d48..00000000 --- a/packages/core/tests/collection/functions/put.function.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Put Function Tests", () => { - // Define Agile - const App = new Agile(); - - interface UserInterface { - id: number; - name: string; - } - - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1]), - group2: collection.Group([1, 2]), - group3: collection.Group([]), - }, - })); - MY_COLLECTION.collect({ id: 1, name: "Frank" }); - MY_COLLECTION.collect({ id: 2, name: "Günter" }); - MY_COLLECTION.collect({ id: 3, name: "Hans" }); - MY_COLLECTION.collect({ id: 4, name: "Michael" }); - - it("Has correct initial Value", () => { - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1, 2]), - "group2 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group3"].value)).to.eq( - JSON.stringify([]), - "group3 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([1, 2, 3, 4]), - "default has correct initial value" - ); - expect(MY_COLLECTION.size).to.eq(4, "MY_COLLECTION has correct size"); - }); - - it("Can put Items into Groups", async () => { - MY_COLLECTION.put([1, 4, 3], ["group1", "group3"]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 4, 3]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1, 2]), - "group2 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group3"].value)).to.eq( - JSON.stringify([1, 4, 3]), - "group3 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([1, 2, 3, 4]), - "default has correct initial value" - ); - }); -}); diff --git a/packages/core/tests/collection/functions/remove.function.spec.ts b/packages/core/tests/collection/functions/remove.function.spec.ts deleted file mode 100644 index 12816abb..00000000 --- a/packages/core/tests/collection/functions/remove.function.spec.ts +++ /dev/null @@ -1,307 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Item } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Remove Function Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - describe("Remove Function 'removeFromGroups'", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1, 2, 3]), - group2: collection.Group([1, 2, 3]), - }, - })); - - // Subscribe Instances for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [ - MY_COLLECTION.getGroup("group1")?.observer, - MY_COLLECTION.getGroup("group2")?.observer, - ]); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group2 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data contains item with id 1" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION data contains item with id 2" - ); - expect(JSON.stringify(MY_COLLECTION.data[3].value)).to.eq( - JSON.stringify({ - id: 3, - name: "frank", - }), - "MY_COLLECTION data contains item with id 3" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(1, "rerenderCount has correct value"); - }); - - it("Can remove item which exist", async () => { - // Remove item - MY_COLLECTION.remove(1).fromGroups("group1"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 3]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group2 has correct value" - ); - expect(MY_COLLECTION.data[1] instanceof Item).to.eq( - true, - "MY_COLLECTION has item with id 1" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can remove items which exist", async () => { - // Remove items - MY_COLLECTION.remove([2, 3]).fromGroups(["group1", "group2"]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1]), - "group2 has correct value" - ); - expect(MY_COLLECTION.data[2] instanceof Item).to.eq( - true, - "MY_COLLECTION has item with id 2" - ); - expect(MY_COLLECTION.data[3] instanceof Item).to.eq( - true, - "MY_COLLECTION has item with id 3" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Can't remove item which doesn't exist", async () => { - // Remove item - MY_COLLECTION.remove(5).fromGroups("group1"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - - it("Can't remove item which doesn't exist in group", async () => { - // Remove item - MY_COLLECTION.remove(1).fromGroups("group1"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 has correct value" - ); - expect(MY_COLLECTION.data[1] instanceof Item).to.eq( - true, - "MY_COLLECTION has item with id 1" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - - it("Can't remove item from not existing group", async () => { - // Remove item - MY_COLLECTION.remove(2).fromGroups("notExisting"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.data[2] instanceof Item).to.eq( - true, - "MY_COLLECTION has item with id 2" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - }); - - describe("Remove Function 'everywhere'", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1, 2, 3]), - group2: collection.Group([1, 2, 3]), - }, - })); - - // Subscribe Instances for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [ - MY_COLLECTION.getGroup("group1")?.observer, - MY_COLLECTION.getGroup("group2")?.observer, - ]); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group2 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data contains item with id 1" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION data contains item with id 2" - ); - expect(JSON.stringify(MY_COLLECTION.data[3].value)).to.eq( - JSON.stringify({ - id: 3, - name: "frank", - }), - "MY_COLLECTION data contains item with id 3" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(1, "rerenderCount has correct value"); - }); - - it("Can remove item which exist", async () => { - // Remove item - MY_COLLECTION.remove(1).everywhere(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 3]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([2, 3]), - "group2 has correct value" - ); - expect(MY_COLLECTION.data[1] instanceof Item).to.eq( - false, - "MY_COLLECTION hasn't item with id 1" - ); - expect(MY_COLLECTION.size).to.eq(2, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can remove items which exist", async () => { - // Remove items - MY_COLLECTION.remove([2, 3]).everywhere(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group2"].value)).to.eq( - JSON.stringify([]), - "group2 has correct value" - ); - expect(MY_COLLECTION.data[2] instanceof Item).to.eq( - false, - "MY_COLLECTION hasn't item with id 2" - ); - expect(MY_COLLECTION.data[3] instanceof Item).to.eq( - false, - "MY_COLLECTION hasn't item with id 3" - ); - expect(MY_COLLECTION.size).to.eq(0, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Can't remove item which doesn't exist", async () => { - // Remove item - MY_COLLECTION.remove(5).everywhere(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.size).to.eq(0, "MY_COLLECTION has correct size"); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - }); -}); diff --git a/packages/core/tests/collection/functions/reset.function.spec.ts b/packages/core/tests/collection/functions/reset.function.spec.ts deleted file mode 100644 index 2ffe5ae8..00000000 --- a/packages/core/tests/collection/functions/reset.function.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Reset Function Tests", () => { - // Define Agile - const App = new Agile(); - - interface UserInterface { - id: string; - name: string; - } - - // Create State - const MY_COLLECTION = App.Collection(); - MY_COLLECTION.collect({ id: "1", name: "frank" }); - MY_COLLECTION.collect({ id: "2", name: "Günter" }); - MY_COLLECTION.collect({ id: "3", name: "Hans" }); - MY_COLLECTION.createGroup("friends", ["1"]); - MY_COLLECTION.createSelector("bestFriend", "1"); - MY_COLLECTION.getGroup("friends").add("3"); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_COLLECTION.data["1"].value)).to.eq( - JSON.stringify({ id: "1", name: "frank" }), - "MY_COLLECTION Item with id 1 exists" - ); - expect(JSON.stringify(MY_COLLECTION.data["2"].value)).to.eq( - JSON.stringify({ id: "2", name: "Günter" }), - "MY_COLLECTION Item with id 2 exists" - ); - expect(JSON.stringify(MY_COLLECTION.data["3"].value)).to.eq( - JSON.stringify({ id: "3", name: "Hans" }), - "MY_COLLECTION Item with id 3 exists" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify(["1", "2", "3"]), - "MY_COLLECTION has friends Group" - ); - expect(JSON.stringify(MY_COLLECTION.groups["friends"].value)).to.eq( - JSON.stringify(["1", "3"]), - "MY_COLLECTION has default Group" - ); - - expect(MY_COLLECTION.selectors["bestFriend"].itemKey).to.eq( - "1", - "MY_COLLECTION has best Friend Selector" - ); - }); - - it("Can reset Collection", async () => { - // Reset Collection - MY_COLLECTION.reset(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.data["2"]).to.eq( - undefined, - "MY_COLLECTION Item with id 2 doesn't exists" - ); - expect(MY_COLLECTION.data["3"]).to.eq( - undefined, - "MY_COLLECTION Item with id 3 doesn't exists" - ); - expect(JSON.stringify(MY_COLLECTION.data["1"].value)).to.eq( - JSON.stringify({ id: "1", dummy: true }), - "MY_COLLECTION Item with id 1 exists as Placeholder because of Selector" - ); - expect(MY_COLLECTION.data["1"].isPlaceholder).to.eq( - true, - "MY_COLLECTION Item with id 1 is Placeholder" - ); - expect(MY_COLLECTION.size).to.eq(0, "MY_COLLECTION size got reset"); - - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([]), - "MY_COLLECTION default Group got reset" - ); - expect(JSON.stringify(MY_COLLECTION.groups["friends"].value)).to.eq( - JSON.stringify(["1"]), - "MY_COLLECTION friends Group got reset" - ); - - expect(MY_COLLECTION.selectors["bestFriend"].itemKey).to.eq( - "1", - "MY_COLLECTION Selector got reset" - ); - }); -}); diff --git a/packages/core/tests/collection/functions/update.function.spec.ts b/packages/core/tests/collection/functions/update.function.spec.ts deleted file mode 100644 index 11cba57d..00000000 --- a/packages/core/tests/collection/functions/update.function.spec.ts +++ /dev/null @@ -1,313 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Update Function Tests", () => { - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Object Interface - interface userInterface { - id: number; - name: string; - age?: number; - } - - // Create Collection - const MY_COLLECTION = App.Collection(); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("default")?.observer]); - - // Collect some items - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "jamie" }, - { id: 3, name: "hans" }, - ]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["default"]?.observer.subs.size === 1).to.eq( - true, - "MY_COLLECTION default Group has correct subs size" - ); - expect(MY_COLLECTION.size).to.eq(3, "MY_COLLECTION has correct size"); - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION item at id 1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "jamie", - }), - "MY_COLLECTION item at id 2 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.data[3].value)).to.eq( - JSON.stringify({ - id: 3, - name: "hans", - }), - "MY_COLLECTION item at id 3 has correct value" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has correct value"); - }); - - it("Can update Item", async () => { - // Update Item - MY_COLLECTION.update(1, { name: "updatedJeff" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[1]?.value)).to.eq( - JSON.stringify({ - id: 1, - name: "updatedJeff", - }), - "MY_COLLECTION data at id 1 has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can update Item and primaryKey", async () => { - // Update Item - MY_COLLECTION.update(3, { id: 4, name: "updatedHans" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[4]?.value)).to.eq( - JSON.stringify({ - id: 4, - name: "updatedHans", - }), - "MY_COLLECTION data switched from id 3 to id 4 and has updated" - ); - expect(MY_COLLECTION.data[3]).to.eq( - undefined, - "MY_COLLECTION item at id 3 doesn't exist anymore" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - expect( - MY_COLLECTION.groups["default"].value.findIndex( - (value) => value === 3 - ) !== -1 - ).to.eq( - false, - "MY_COLLECTION item with id 3 doesn't exist in default group anymore" - ); - expect( - MY_COLLECTION.groups["default"].value.findIndex( - (value) => value === 4 - ) !== -1 - ).to.eq(true, "MY_COLLECTION item with id 3 does exist in default group"); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Can't update not existing Item", async () => { - // Update Item - MY_COLLECTION.update(5, { name: "updatedName" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has stayed the same"); - }); - - it("Can't update Item with the same value", async () => { - // Update Item - MY_COLLECTION.update(1, { name: "updatedJeff" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[1]?.value)).to.eq( - JSON.stringify({ - id: 1, - name: "updatedJeff", - }), - "MY_COLLECTION data at id 1 stayed the same" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has stayed the same"); - }); - - it("Can't update Item with non object changes", async () => { - // Update Item - // @ts-ignore - MY_COLLECTION.update(1, "hi"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has stayed the same"); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by updating Item with background = false", async () => { - // Update Item - MY_COLLECTION.update(2, { name: "updatedJamie" }, { background: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - name: "updatedJamie", - }), - "MY_COLLECTION data at id 2 has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by updating Item with background = true", async () => { - // Update Item - MY_COLLECTION.update( - 4, - { id: 4, name: "updatedHans2" }, - { background: true } - ); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[4]?.value)).to.eq( - JSON.stringify({ - id: 4, - name: "updatedHans2", - }), - "MY_COLLECTION data at id 4 has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount stayed the same"); - }); - - it("Doesn't call callBackFunction by updating Item and primaryKey with background = true", async () => { - // Update Item - MY_COLLECTION.update( - 4, - { id: 3, name: "updatedHans3" }, - { background: true } - ); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[3]?.value)).to.eq( - JSON.stringify({ - id: 3, - name: "updatedHans3", - }), - "MY_COLLECTION data switched from id 4 to id 3 and has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount stayed the same"); - }); - }); - - describe("Test addNewProperties option", () => { - it("Doesn't add property to value with addNewProperties = false", async () => { - // Update Item - MY_COLLECTION.update( - 2, - { name: "updatedJamie2", age: 10 }, - { addNewProperties: false } - ); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - name: "updatedJamie2", - }), - "MY_COLLECTION data at id 2 has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(5, "rerenderCount has been increased by 1"); - }); - - it("Does add property to value with addNewProperties = true", async () => { - // Update Item - MY_COLLECTION.update( - 2, - { name: "updatedJamie3", age: 20 }, - { addNewProperties: true } - ); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.data[2]?.value)).to.eq( - JSON.stringify({ - id: 2, - name: "updatedJamie3", - age: 20, - }), - "MY_COLLECTION data at id 2 has updated" - ); - expect(Object.keys(MY_COLLECTION.data).length).to.eq( - 3, - "MY_COLLECTION has still all items" - ); - - expect(rerenderCount).to.eq(6, "rerenderCount has been increased by 1"); - }); - }); -}); diff --git a/packages/core/tests/collection/group/default.spec.ts b/packages/core/tests/collection/group/default.spec.ts deleted file mode 100644 index fe85c441..00000000 --- a/packages/core/tests/collection/group/default.spec.ts +++ /dev/null @@ -1,290 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Default Group Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - describe("Group", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group(), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("group1")?.observer]); - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["default"]?.observer.subs.size === 0).to.eq( - true, - "MY_COLLECTION default Group has correct subs size" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([]), - "default Group has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].output)).to.eq( - JSON.stringify([]), - "default Group has correct initial output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].items)).to.eq( - JSON.stringify([]), - "default Group has correct initial states" - ); - expect(MY_COLLECTION.groups["default"].key).to.eq( - "default", - "group1 Group has correct initial key" - ); - - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(MY_COLLECTION.groups["group1"]?.observer.subs.size === 1).to.eq( - true, - "MY_COLLECTION group1 Group has correct subs size" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 Group has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([]), - "group1 Group has correct initial output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].items)).to.eq( - JSON.stringify([]), - "group1 Group has correct initial states" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "group1", - "group1 Group has correct initial key" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount has correct value"); - }); - - it("Has correct values after collecting items", async () => { - // Collect Data - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "default Group has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].output)).to.eq( - JSON.stringify([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]), - "default Group has correct output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].items)).to.eq( - JSON.stringify([ - MY_COLLECTION.getItem(1), - MY_COLLECTION.getItem(2), - MY_COLLECTION.getItem(3), - ]), - "default Group has correct states" - ); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 Group has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([]), - "group1 Group has correct output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].items)).to.eq( - JSON.stringify([]), - "group1 Group has correct states" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount stayed the same"); - }); - }); - - describe("Group with initial values", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1, 2, 3]), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("group1")?.observer]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["default"] instanceof Group).to.eq( - true, - "MY_COLLECTION default Group has been created" - ); - expect(MY_COLLECTION.groups["default"]?.observer.subs.size === 0).to.eq( - true, - "MY_COLLECTION default Group has correct subs size" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([]), - "default has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].output)).to.eq( - JSON.stringify([]), - "default has correct initial output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].items)).to.eq( - JSON.stringify([]), - "default has correct initial states" - ); - expect(MY_COLLECTION.groups["default"].key).to.eq( - "default", - "default Group has correct initial key" - ); - - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(MY_COLLECTION.groups["group1"]?.observer.subs.size === 1).to.eq( - true, - "MY_COLLECTION group1 Group has correct subs size" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 Group has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([]), - "group1 Group has correct initial output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].items)).to.eq( - JSON.stringify([]), - "group1 Group has correct initial states" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "group1", - "group1 Group has correct initial key" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount has correct value"); - }); - - it("Has correct values after collecting items", async () => { - // Collect Data - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["default"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "default has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].output)).to.eq( - JSON.stringify([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]), - "default has correct output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["default"].items)).to.eq( - JSON.stringify([ - MY_COLLECTION.getItem(1), - MY_COLLECTION.getItem(2), - MY_COLLECTION.getItem(3), - ]), - "default has correct states" - ); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]), - "group1 has correct output" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].items)).to.eq( - JSON.stringify([ - MY_COLLECTION.getItem(1), - MY_COLLECTION.getItem(2), - MY_COLLECTION.getItem(3), - ]), - "group1 has correct states" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - }); - - describe("Group with key", () => { - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([], { key: "mywierdgroup" }), - }, - })); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(MY_COLLECTION.groups["group1"].key).to.eq( - "mywierdgroup", - "group1 has correct key" - ); - }); - }); -}); diff --git a/packages/core/tests/collection/group/functions/add.function.spec.ts b/packages/core/tests/collection/group/functions/add.function.spec.ts deleted file mode 100644 index 89b70a37..00000000 --- a/packages/core/tests/collection/group/functions/add.function.spec.ts +++ /dev/null @@ -1,321 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../../src"; -import testIntegration from "../../../test.integration"; - -describe("Add function Tests", () => { - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([2]), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("group1")?.observer]); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - { id: 6, name: "livia" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - ]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([{ id: 2, name: "hans" }]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been correct value"); - }); - - it("Can add Group", async () => { - MY_COLLECTION.groups["group1"].add(1); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - ]), - "group1 has correct output" - ); - expect( - JSON.stringify(MY_COLLECTION.groups["group1"].notFoundItemKeys) - ).to.eq(JSON.stringify([]), "group1 has correct notFoundPrimaryKeys"); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can add multiple Groups", async () => { - MY_COLLECTION.groups["group1"].add([7, 8]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - ]), - "group1 has correct output" - ); - expect( - JSON.stringify(MY_COLLECTION.groups["group1"].notFoundItemKeys) - ).to.eq(JSON.stringify([]), "group1 has correct notFoundPrimaryKeys"); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Can't add item to Group which already exists", async () => { - MY_COLLECTION.groups["group1"].add(1); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - - it("Can add item to Group which doesn't exist in collection", async () => { - MY_COLLECTION.groups["group1"].add(100); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8, 100]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - ]), - "group1 has correct output" - ); - expect( - JSON.stringify(MY_COLLECTION.groups["group1"].notFoundItemKeys) - ).to.eq(JSON.stringify([100]), "group1 has correct notFoundPrimaryKeys"); - - expect(rerenderCount).to.eq( - 3, - "rerenderCount stayed the same because output won't change -> rerender not necessary" - ); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by adding Item to group with background = false", async () => { - MY_COLLECTION.groups["group1"].add(3, { background: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8, 100, 3]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 3, name: "frank" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by adding Item to group with background = true", async () => { - MY_COLLECTION.groups["group1"].add(4, { background: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8, 100, 3, 4]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount stayed the same"); - }); - }); - - describe("Test method option", () => { - it("Does add the item at the end of the group with method = 'push'", async () => { - MY_COLLECTION.groups["group1"].add(5, { method: "push" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 1, 7, 8, 100, 3, 4, 5]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(5, "rerenderCount has been increased by 1"); - }); - - it("Does add the item at the start of the group with method = 'unshift'", async () => { - MY_COLLECTION.groups["group1"].add(6, { method: "unshift" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([6, 2, 1, 7, 8, 100, 3, 4, 5]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 6, name: "livia" }, - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(6, "rerenderCount has been increased by 1"); - }); - }); - - describe("Test overwrite option", () => { - it("Leave existing item at current position and doesn't overwrite it with overwrite = false", async () => { - MY_COLLECTION.groups["group1"].add(3, { overwrite: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([6, 2, 1, 7, 8, 100, 3, 4, 5]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 6, name: "livia" }, - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(6, "rerenderCount stayed the same"); - }); - - it("Overwrites existing item and add it at new position with overwrite = true", async () => { - MY_COLLECTION.groups["group1"].add(3, { overwrite: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([6, 2, 1, 7, 8, 100, 4, 5, 3]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 6, name: "livia" }, - { id: 2, name: "hans" }, - { id: 1, name: "jeff" }, - { id: 7, name: "joshi" }, - { id: 8, name: "günter" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - { id: 3, name: "frank" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(7, "rerenderCount has been increased by 1"); - }); - }); -}); diff --git a/packages/core/tests/collection/group/functions/has.function.spec.ts b/packages/core/tests/collection/group/functions/has.function.spec.ts deleted file mode 100644 index 64bb2cc0..00000000 --- a/packages/core/tests/collection/group/functions/has.function.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { expect } from "chai"; -import { Agile, Group } from "../../../../src"; - -describe("Has function Tests", () => { - // Define Agile - const App = new Agile(); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1, 2, 3]), - }, - })); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - ]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - ]), - "group1 has correct output" - ); - }); - - it("Can use has function", () => { - const has = MY_COLLECTION.groups["group1"].has(1); - - expect(has).to.eq(true); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - }); - - it("Can use has function with not existing primaryKey in collection and group", () => { - const has = MY_COLLECTION.groups["group1"].has(100); - - expect(has).to.eq(false); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - }); - - it("Can use has function with not existing primaryKey in group", () => { - const has = MY_COLLECTION.groups["group1"].has(4); - - expect(has).to.eq(false); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3]), - "group1 has correct initial value" - ); - }); -}); diff --git a/packages/core/tests/collection/group/functions/remove.function.spec.ts b/packages/core/tests/collection/group/functions/remove.function.spec.ts deleted file mode 100644 index 30a4b982..00000000 --- a/packages/core/tests/collection/group/functions/remove.function.spec.ts +++ /dev/null @@ -1,163 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Group } from "../../../../src"; -import testIntegration from "../../../test.integration"; - -describe("Remove function Tests", () => { - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - groups: { - group1: collection.Group([1, 2, 3, 4, 5]), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getGroup("group1")?.observer]); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.groups["group1"] instanceof Group).to.eq( - true, - "MY_COLLECTION group1 Group has been created" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([1, 2, 3, 4, 5]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has correct value"); - }); - - it("Can remove item which exists", async () => { - MY_COLLECTION.groups["group1"].remove(1); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([2, 3, 4, 5]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 2, name: "hans" }, - { id: 3, name: "frank" }, - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can remove multiple items which exist", async () => { - MY_COLLECTION.groups["group1"].remove([2, 3]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([4, 5]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Can't remove item which doesn't exist", async () => { - MY_COLLECTION.groups["group1"].remove(100); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([4, 5]), - "group1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([ - { id: 4, name: "gina" }, - { id: 5, name: "tabea" }, - ]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by removing Item from group with background = false", async () => { - MY_COLLECTION.groups["group1"].remove(4, { background: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([5]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([{ id: 5, name: "tabea" }]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by removing Item from group with background = true", async () => { - MY_COLLECTION.groups["group1"].remove(5, { background: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_COLLECTION.groups["group1"].value)).to.eq( - JSON.stringify([]), - "group1 has correct value" - ); - expect(JSON.stringify(MY_COLLECTION.groups["group1"].output)).to.eq( - JSON.stringify([]), - "group1 has correct output" - ); - - expect(rerenderCount).to.eq(4, "rerenderCount stayed the same"); - }); - }); -}); diff --git a/packages/core/tests/collection/selector/default.spec.ts b/packages/core/tests/collection/selector/default.spec.ts deleted file mode 100644 index 26e4c501..00000000 --- a/packages/core/tests/collection/selector/default.spec.ts +++ /dev/null @@ -1,170 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Selector, Item } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Default Selector Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - describe("Selector", () => { - let rerenderCount = 0; - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - selectors: { - selector1: collection.Selector(1), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getSelectorWithReference("selector1").observer]); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 Selector has been created" - ); - expect( - MY_COLLECTION.selectors["selector1"]?.observer.subs.size === 1 - ).to.eq(true, "MY_COLLECTION selector1 Selector has correct subs size"); - expect(MY_COLLECTION.selectors["selector1"].key).to.eq( - "selector1", - "selector1 Selector has correct initial key" - ); - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 1, - "selector1 Selector has correct initial id" - ); - expect(MY_COLLECTION.selectors["selector1"].exists).to.eq( - false, - "selector1 Selector does exist" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify(undefined), - "selector1 Selector has correct initial value" - ); - - expect(MY_COLLECTION.data[1] instanceof Item).to.eq( - true, - "MY_COLLECTION data at id 1 has been created" - ); - expect(MY_COLLECTION.data[1].exists).to.eq( - false, - "MY_COLLECTION data at id 1 doesn't exist" - ); - expect(MY_COLLECTION.data[1].key).to.eq( - 1, - "MY_COLLECTION data at id 1 has correct initial key" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - dummy: true, - }), - "MY_COLLECTION data at id 1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - dummy: true, - }), - "MY_COLLECTION data at id 1 has correct previousStateValue" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].initialStateValue)).to.eq( - JSON.stringify({ - id: 1, - dummy: true, - }), - "MY_COLLECTION data at id 1 has correct initialStateValue" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount has correct value"); - }); - - it("Has correct values after collecting items", async () => { - // Collect Data - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - ]); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].exists).to.eq( - true, - "selector1 Selector exists" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "selector1 Selector has correct initial value" - ); - - expect(MY_COLLECTION.data[1].exists).to.eq( - true, - "MY_COLLECTION data at id 1 exists" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data at id 1 has correct initial value" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data at id 1 has correct previousStateValue" - ); - expect(JSON.stringify(MY_COLLECTION.data[1].initialStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION data at id 1 has correct initialStateVaoue" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - }); - - describe("Selector with key", () => { - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - selectors: { - selector1: collection.Selector(1, { key: "mywierdselector" }), - }, - })); - - it("Has correct initial values", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "MY_COLLECTION selector1 Selector has been created" - ); - expect(MY_COLLECTION.selectors["selector1"].key).to.eq( - "mywierdselector", - "selector1 has correct key" - ); - }); - }); -}); diff --git a/packages/core/tests/collection/selector/functions/select.function.spec.ts b/packages/core/tests/collection/selector/functions/select.function.spec.ts deleted file mode 100644 index 3db0ea3e..00000000 --- a/packages/core/tests/collection/selector/functions/select.function.spec.ts +++ /dev/null @@ -1,199 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Selector, Item } from "../../../../src"; -import testIntegration from "../../../test.integration"; - -describe("select Function Tests", () => { - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create Collection - const MY_COLLECTION = App.Collection((collection) => ({ - selectors: { - selector1: collection.Selector(1), - }, - })); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COLLECTION.getSelector("selector1")?.observer]); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - ]); - - it("Has initial value", () => { - expect(MY_COLLECTION.selectors["selector1"] instanceof Selector).to.eq( - true, - "selector1 Selector exists" - ); - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 1, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "selector1 Selector has correct initial value" - ); - - expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_COLLECTION at id 1 has correct data" - ); - expect(JSON.stringify(MY_COLLECTION.data[2].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "MY_COLLECTION at id 2 has correct data" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has correct value"); - }); - - it("Can select another id which doesn't exist", async () => { - MY_COLLECTION.selectors["selector1"].select(100); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 100, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify(undefined), - "selector1 Selector has correct value" - ); - - expect(MY_COLLECTION.data[100] instanceof Item).to.eq( - true, - "MY_COLLECTION at id 100 has been created as placeholder.. to hold connection" - ); - expect(JSON.stringify(MY_COLLECTION.data[100].value)).to.eq( - JSON.stringify({ - id: 100, - dummy: true, - }), - "MY_COLLECTION at id 100 has correct value" - ); - expect(MY_COLLECTION.data[100].exists).to.eq( - false, - "MY_COLLECTION at id 100 doesn't exist" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount stayed the same"); - }); - - it("Can select another id", async () => { - MY_COLLECTION.selectors["selector1"].select(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 2, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "selector1 Selector has correct value" - ); - - expect(MY_COLLECTION.data[100] instanceof Item).to.eq( - false, - "MY_COLLECTION at id 100 has been removed" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can't select the same id", async () => { - MY_COLLECTION.selectors["selector1"].select(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 2, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "selector1 Selector has correct value" - ); - - expect(MY_COLLECTION.data[100] instanceof Item).to.eq( - false, - "MY_COLLECTION at id 100 has been removed" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount stayed the same"); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by selecting new id with background = false", async () => { - MY_COLLECTION.selectors["selector1"].select(1, { background: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 1, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "selector1 Selector has correct value" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by selecting new id with background = true", async () => { - MY_COLLECTION.selectors["selector1"].select(2, { background: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COLLECTION.selectors["selector1"].itemKey).to.eq( - 2, - "selector1 Selector is watching correct id" - ); - expect(JSON.stringify(MY_COLLECTION.selectors["selector1"].value)).to.eq( - JSON.stringify({ - id: 2, - name: "hans", - }), - "selector1 Selector has correct value" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - }); -}); diff --git a/packages/core/tests/computed/default.spec.ts b/packages/core/tests/computed/default.spec.ts deleted file mode 100644 index b02c783b..00000000 --- a/packages/core/tests/computed/default.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Computed } from "../../src"; -import testIntegration from "../test.integration"; - -describe("Default Computed Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - interface userInterface { - id: number; - name: string; - } - - describe("Computed", () => { - let computedCallCount = 0; - let rerenderCount = 0; - - // Create State - const MY_STATE = App.State("hello"); - const MY_STATE_2 = App.State("bye"); - - // Create Collection - const MY_COLLECTION = App.Collection({ - groups: ["group1", "group2"], - selectors: ["selector1", "selector2"], - }); - - // Create Selectors - const MY_SELECTOR = MY_COLLECTION.getSelector("selector1")?.select(1); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - ]); - - // Create Computed - const MY_COMPUTED = App.Computed(() => { - computedCallCount++; - return `${MY_STATE.value}_${MY_STATE_2.value}_${ - MY_SELECTOR?.value?.name - }_${MY_COLLECTION.getItem(2)?.value.name}`; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COMPUTED.observer]); - - it("Has correct initial values", () => { - expect(MY_COMPUTED instanceof Computed).to.eq( - true, - "MY_COMPUTED is computed" - ); - expect(MY_COMPUTED.key).to.eq( - undefined, - "MY_COMPUTED has correct initial key" - ); - expect(MY_COMPUTED.value).to.eq( - "hello_bye_jeff_hans", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - // @ts-ignore - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([ - MY_STATE.observer, - MY_STATE_2.observer, - MY_SELECTOR.observer, - MY_COLLECTION.getItem(2).observer, - ]), - "MY_COMPUTED has correct deps" - ); - expect(computedCallCount).to.eq( - 2, - "computedCallCount has correct initial value" - ); - expect(rerenderCount).to.eq(0, "rerenderCount has correct initial value"); - }); - - it("Does call computed Function if updating item", async () => { - // Update State - MY_STATE_2.set("hehe"); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "hello_hehe_jeff_hans", - "MY_COMPUTED has correct value" - ); - expect(computedCallCount).to.eq( - 3, - "computedCallCount has been increased by 1" - ); - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - - // Update Collection - MY_COLLECTION.update(2, { name: "test" }); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "hello_hehe_jeff_test", - "MY_COMPUTED has correct value" - ); - expect(computedCallCount).to.eq( - 4, - "computedCallCount has been increased by 1" - ); - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - - // Update Collection - MY_COLLECTION.update(1, { name: "fun" }); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "hello_hehe_fun_test", - "MY_COMPUTED has correct value" - ); - expect(computedCallCount).to.eq( - 5, - "computedCallCount has been increased by 1" - ); - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - }); - - describe("Computed with initial subs", () => { - let computedCallCount = 0; - let rerenderCount = 0; - - // Create State - const MY_STATE = App.State("hello"); - const MY_STATE_2 = App.State("bye"); - const MY_STATE_3 = App.State("test"); - - // Create Collection - const MY_COLLECTION = App.Collection({ - groups: ["group1", "group2"], - selectors: ["selector1", "selector2"], - }); - - // Create Selectors - const MY_SELECTOR = MY_COLLECTION.getSelector("selector1")?.select(1); - - MY_COLLECTION.collect([ - { id: 1, name: "jeff" }, - { id: 2, name: "hans" }, - ]); - - // Create Computed - const MY_COMPUTED = App.Computed(() => { - computedCallCount++; - return `${MY_STATE.value}_${MY_STATE_2.value}_${ - MY_SELECTOR?.value?.name - }_${MY_COLLECTION.getItem(2)?.value.name}`; - }, [MY_STATE_3]); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COMPUTED.observer]); - - it("Has correct initial values", () => { - expect(MY_COMPUTED instanceof Computed).to.eq( - true, - "MY_COMPUTED is computed" - ); - expect(MY_COMPUTED.key).to.eq( - undefined, - "MY_COMPUTED has correct initial key" - ); - expect(MY_COMPUTED.value).to.eq( - "hello_bye_jeff_hans", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([MY_STATE_3.observer]), - "MY_COMPUTED has correct hardCodedDeps" - ); - // @ts-ignore - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([ - MY_STATE_3.observer, - MY_STATE.observer, - MY_STATE_2.observer, - MY_SELECTOR.observer, - MY_COLLECTION.getItem(2).observer, - ]), - "MY_COMPUTED has correct deps" - ); - expect(computedCallCount).to.eq( - 2, - "computedCallCount has correct initial value" - ); - expect(rerenderCount).to.eq(0, "rerenderCount has correct initial value"); - }); - - it("Does call computed Function if updating item", async () => { - // Update State - MY_STATE_3.set("changed"); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE_3.value).to.eq("changed", "MY_STATE_3 has correct value"); - expect(MY_COMPUTED.value).to.eq( - "hello_bye_jeff_hans", - "MY_COMPUTED has correct value" - ); - expect(computedCallCount).to.eq( - 3, - "computedCallCount has been increased by 1" - ); - expect(rerenderCount).to.eq( - 0, - "rerenderCount stayed the same because computed hasn't changed" - ); - }); - }); -}); diff --git a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts b/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts deleted file mode 100644 index a7bca59d..00000000 --- a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts +++ /dev/null @@ -1,191 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Computed } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("updateComputeFunction Function test_integration", () => { - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Create State - const MY_STATE = App.State("hello"); - const MY_STATE_2 = App.State("bye"); - const MY_STATE_3 = App.State("jeff"); - const MY_STATE_4 = App.State("hans"); - - // Create Computed - const MY_COMPUTED = App.Computed(() => { - return `${MY_STATE.value}_${MY_STATE_2.value}`; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_COMPUTED.observer]); - - it("Has correct initial values", () => { - expect(MY_COMPUTED instanceof Computed).to.eq( - true, - "MY_COMPUTED is computed" - ); - expect(MY_COMPUTED.key).to.eq( - undefined, - "MY_COMPUTED has correct initial key" - ); - expect(MY_COMPUTED.value).to.eq( - "hello_bye", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([MY_STATE.observer, MY_STATE_2.observer]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount has correct initial value"); - }); - - it("Can update compute Function", async () => { - // Update compute Function - MY_COMPUTED.updateComputeFunction(() => { - return `${MY_STATE_3.value}_${MY_STATE_4.value}`; - }); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "jeff_hans", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([MY_STATE_3.observer, MY_STATE_4.observer]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - - it("Can update compute Function with hardCodedDeps", async () => { - // Update compute Function - MY_COMPUTED.updateComputeFunction(() => { - return `${MY_STATE_2.value}_${MY_STATE_3.value}`; - }, [MY_STATE_4]); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "bye_jeff", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([MY_STATE_4.observer]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([ - MY_STATE_4.observer, - MY_STATE_2.observer, - MY_STATE_3.observer, - ]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Can't update compute Function with the same value", async () => { - // Update compute Function - MY_COMPUTED.updateComputeFunction(() => { - return `${MY_STATE_2.value}_${MY_STATE_3.value}`; - }, []); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "bye_jeff", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([MY_STATE_2.observer, MY_STATE_3.observer]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(2, "rerenderCount stayed the same"); - }); - - describe("Test background option", () => { - it("Does call callBackFunction by updating computeFunction with background = false", async () => { - // Update compute Function - MY_COMPUTED.updateComputeFunction( - () => { - return `${MY_STATE.value}_${MY_STATE_3.value}`; - }, - [], - { background: false } - ); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "hello_jeff", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([MY_STATE.observer, MY_STATE_3.observer]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call callBackFunction by updating computeFunction with background = true", async () => { - // Update compute Function - MY_COMPUTED.updateComputeFunction( - () => { - return `${MY_STATE_4.value}_${MY_STATE_2.value}`; - }, - [], - { background: true } - ); - - // Needs some time to call computed - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_COMPUTED.value).to.eq( - "hans_bye", - "MY_COMPUTED has correct value" - ); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq( - JSON.stringify([]), - "MY_COMPUTED has correct hardCodedDeps" - ); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq( - JSON.stringify([MY_STATE_4.observer, MY_STATE_2.observer]), - "MY_COMPUTED has correct deps" - ); - - expect(rerenderCount).to.eq(3, "rerenderCount stayed the same"); - }); - }); -}); diff --git a/packages/core/tests/event/default.spec.ts b/packages/core/tests/event/default.spec.ts deleted file mode 100644 index 066d4d14..00000000 --- a/packages/core/tests/event/default.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Event } from "../../src"; -import testIntegration from "../test.integration"; - -describe("Default Event Tests", () => { - // Define Agile - const App = new Agile().use(testIntegration); - - interface EventPayload { - title: string; - message: string; - } - - describe("Event", () => { - // Create Event - const MY_EVENT = App.Event(); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(JSON.stringify(MY_EVENT.callbacks)).to.eq( - JSON.stringify({}), - "MY_EVENT has no callback" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - expect(MY_EVENT.key).to.eq(undefined, "MY_EVENT has correct key"); - expect(MY_EVENT._key).to.eq(undefined, "MY_EVENT has correct _key"); - }); - - it("Can change key", () => { - // Update key - MY_EVENT.key = "myKey"; - - expect(MY_EVENT.key).to.eq("myKey", "MY_EVENT has correct key"); - expect(MY_EVENT._key).to.eq("myKey", "MY_EVENT has correct _key"); - }); - }); - - describe("Event with key", () => { - // Create Event - const MY_EVENT = App.Event({ key: "myKey" }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ - rerender: false, - }), - "MY_EVENT has correct initial config" - ); - expect(JSON.stringify(MY_EVENT.callbacks)).to.eq( - JSON.stringify({}), - "MY_EVENT has no callback" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - expect(MY_EVENT.key).to.eq("myKey", "MY_EVENT has correct key"); - expect(MY_EVENT._key).to.eq("myKey", "MY_EVENT has correct _key"); - }); - - it("Can change key", () => { - // Update key - MY_EVENT.key = "myNewKey"; - - expect(MY_EVENT.key).to.eq("myNewKey", "MY_EVENT has correct key"); - expect(MY_EVENT._key).to.eq("myNewKey", "MY_EVENT has correct _key"); - }); - }); - - describe("Event with enabled = false", () => { - let eventCallCount = 0; - let currentEventPayload; - - // Create Event - const MY_EVENT = App.Event({ enabled: false }); - - MY_EVENT.on("myKey", (payload) => { - eventCallCount++; - currentEventPayload = payload; - }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.callbacks["myKey"] !== undefined).to.eq( - true, - "MY_EVENT has 'myKey' Callback" - ); - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - - expect(eventCallCount).to.eq( - 0, - "eventCallCount has correct initial value" - ); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload has correct initial value" - ); - }); - - it("Doesn't call event callbacks", () => { - // Trigger Event - MY_EVENT.trigger({ title: "test", message: "messageTest" }); - - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses stayed the same"); - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - - expect(eventCallCount).to.eq(0, "eventCallCount stayed the same"); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload stayed the same" - ); - }); - }); - - describe("Event with delay", () => { - let eventCallCount = 0; - let currentEventPayload; - - // Create Event - const MY_EVENT = App.Event({ delay: 1000 }); - - MY_EVENT.on("myEvent123", (payload) => { - eventCallCount++; - currentEventPayload = payload; - }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ - rerender: false, - delay: 1000, - }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.callbacks["myEvent123"] !== undefined).to.eq( - true, - "MY_EVENT has 'myEvent123' Callback" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq( - 0, - "eventCallCount has correct initial value" - ); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload has correct initial value" - ); - }); - - it("Does call callbacks with delay", async () => { - // Trigger Event - MY_EVENT.trigger({ title: "test", message: "messageTest" }); - - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses stayed the same"); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq(0, "eventCallCount stayed the same"); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload stayed the same" - ); - - // Wait one second - await new Promise((resolve) => setTimeout(resolve, 1000)); - - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses has been increased by 1"); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq(1, "eventCallCount has been increased by 1"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "test", - message: "messageTest", - }), - "currentEventPayload has the correct value" - ); - }); - }); - - describe("Event with maxUses", () => { - let eventCallCount = 0; - let currentEventPayload; - - // Create Event - const MY_EVENT = App.Event({ maxUses: 3 }); - - MY_EVENT.on("myEvent321", (payload) => { - eventCallCount++; - currentEventPayload = payload; - }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ - rerender: false, - maxUses: 3, - }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.callbacks["myEvent321"] !== undefined).to.eq( - true, - "MY_EVENT has 'myEvent321' Callback" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq( - 0, - "eventCallCount has correct initial value" - ); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload has correct initial value" - ); - }); - - it("Get disabled after 3 uses", () => { - // Trigger Event - MY_EVENT.trigger({ title: "test", message: "messageTest" }); - - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses has been increased by 1"); - expect(eventCallCount).to.eq(1, "eventCallCount has been increased by 1"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "test", - message: "messageTest", - }), - "currentEventPayload has correct value" - ); - - // Trigger Event - MY_EVENT.trigger({ title: "test2", message: "messageTest2" }); - - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - expect(MY_EVENT.uses).to.eq(2, "MY_EVENT uses has been increased by 1"); - expect(eventCallCount).to.eq(2, "eventCallCount has been increased by 1"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "test2", - message: "messageTest2", - }), - "currentEventPayload has correct value" - ); - - // Trigger Event - MY_EVENT.trigger({ title: "test3", message: "messageTest3" }); - - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT got disabled"); - expect(MY_EVENT.uses).to.eq(3, "MY_EVENT uses has been increased by 1"); - expect(eventCallCount).to.eq(3, "eventCallCount has been increased by 1"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "test3", - message: "messageTest3", - }), - "currentEventPayload has correct value" - ); - - // Trigger Event - MY_EVENT.trigger({ title: "test4", message: "messageTest4" }); - - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - expect(MY_EVENT.uses).to.eq(3, "MY_EVENT uses stayed the same"); - expect(eventCallCount).to.eq(3, "eventCallCount stayed the same"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "test3", - message: "messageTest3", - }), - "currentEventPayload stayed the same" - ); - }); - }); - - describe("Event with rerender = true", () => { - let rerenderCount = 0; - - // Create Event - const MY_EVENT = App.Event({ rerender: true }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_EVENT.observer]); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: true }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(rerenderCount).to.eq(0, "rerenderCount has correct initial value"); - }); - - it("Does rerender on trigger", async () => { - // Trigger Event - MY_EVENT.trigger({ title: "test", message: "messageTest" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses stayed the same"); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - }); -}); diff --git a/packages/core/tests/event/functions/disable.function.spec.ts b/packages/core/tests/event/functions/disable.function.spec.ts deleted file mode 100644 index 531f603f..00000000 --- a/packages/core/tests/event/functions/disable.function.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Disable Function Tests", () => { - // Define Agile - const App = new Agile(); - - interface EventPayload { - title: string; - message: string; - } - - // Create Event - const MY_EVENT = App.Event(); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - }); - - it("Can disable Event", () => { - // Disable Event - MY_EVENT.disable(); - - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses stayed the same"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct config" - ); - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - }); -}); diff --git a/packages/core/tests/event/functions/enable.function.spec.ts b/packages/core/tests/event/functions/enable.function.spec.ts deleted file mode 100644 index 00d06c18..00000000 --- a/packages/core/tests/event/functions/enable.function.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Enable Function Tests", () => { - // Define Agile - const App = new Agile(); - - interface EventPayload { - title: string; - message: string; - } - - // Create Event - const MY_EVENT = App.Event({ enabled: false }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - }); - - it("Can enable Event", () => { - // Enable Event - MY_EVENT.enable(); - - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses stayed the same"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct config" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - }); -}); diff --git a/packages/core/tests/event/functions/on.function.spec.ts b/packages/core/tests/event/functions/on.function.spec.ts deleted file mode 100644 index 989d45ec..00000000 --- a/packages/core/tests/event/functions/on.function.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("On Function Tests", () => { - let eventCallCount = 0; - let currentEventPayload; - - // Define Agile - const App = new Agile(); - - interface EventPayload { - title: string; - message: string; - } - - // Create Event - const MY_EVENT = App.Event(); - - MY_EVENT.on("myEvent", (payload) => { - eventCallCount++; - currentEventPayload = payload; - }); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.callbacks["myEvent"] !== undefined).to.eq( - true, - "MY_EVENT has 'myEvent' in callbacks" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq(0, "eventCallCount has correct initial size"); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload has correct initial value" - ); - }); - - it("Does call on callback by triggering Event", async () => { - // Trigger Event - MY_EVENT.trigger({ title: "Hello", message: "There" }); - - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses has been increased by 1"); - - expect(eventCallCount).to.eq(1, "eventCallCount has been increased by 2"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "Hello", - message: "There", - }), - "currentEventPayload has correct value" - ); - }); -}); diff --git a/packages/core/tests/event/functions/reset.function.spec.ts b/packages/core/tests/event/functions/reset.function.spec.ts deleted file mode 100644 index a51b0a2f..00000000 --- a/packages/core/tests/event/functions/reset.function.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Reset Function Tests", () => { - // Define Agile - const App = new Agile(); - - interface EventPayload { - title: string; - message: string; - } - - // Create Event - const MY_EVENT = App.Event(); - - // Trigger and disable Event - MY_EVENT.trigger(undefined); - MY_EVENT.trigger(undefined); - MY_EVENT.disable(); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(2, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.enabled).to.eq(false, "MY_EVENT is disabled"); - }); - - it("Can reset Event", () => { - // Reset Event - MY_EVENT.reset(); - - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has been reset"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct config" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - }); -}); diff --git a/packages/core/tests/event/functions/trigger.function.spec.ts b/packages/core/tests/event/functions/trigger.function.spec.ts deleted file mode 100644 index 5fd4f9f9..00000000 --- a/packages/core/tests/event/functions/trigger.function.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Trigger Function Tests", () => { - let eventCallCount = 0; - let currentEventPayload; - let rerenderCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - interface EventPayload { - title: string; - message: string; - } - - // Create Event - const MY_EVENT = App.Event(); - - MY_EVENT.on("myEvent", (payload) => { - eventCallCount++; - currentEventPayload = payload; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_EVENT.observer]); - - it("Has correct initial value", () => { - expect(MY_EVENT.uses).to.eq(0, "MY_EVENT uses has correct initial value"); - expect(JSON.stringify(MY_EVENT.config)).to.eq( - JSON.stringify({ rerender: false }), - "MY_EVENT has correct initial config" - ); - expect(MY_EVENT.callbacks["myEvent"] !== undefined).to.eq( - true, - "MY_EVENT has 'myEvent' in callbacks" - ); - expect(MY_EVENT.enabled).to.eq(true, "MY_EVENT is enabled"); - - expect(eventCallCount).to.eq(0, "eventCallCount has correct initial size"); - expect(rerenderCount).to.eq(0, "rerenderCount has correct initial size"); - expect(currentEventPayload).to.eq( - undefined, - "currentEventPayload has correct initial value" - ); - }); - - it("Can trigger enabled Event", async () => { - // Trigger Event - MY_EVENT.trigger({ title: "Hello", message: "There" }); - - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses has been increased by 1"); - - expect(eventCallCount).to.eq(1, "eventCallCount has been increased by 1"); - expect(rerenderCount).to.eq(0, "rerenderCount stayed the same"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "Hello", - message: "There", - }), - "currentEventPayload has correct value" - ); - }); - - it("Can't trigger disabled Event", async () => { - // Disable Event - MY_EVENT.enabled = false; - - // Trigger Event - MY_EVENT.trigger({ title: "Hello", message: "There" }); - - expect(MY_EVENT.uses).to.eq(1, "MY_EVENT uses stayed the same"); - - expect(eventCallCount).to.eq(1, "eventCallCount stayed the same"); - expect(rerenderCount).to.eq(0, "rerenderCount stayed the same"); - expect(JSON.stringify(currentEventPayload)).to.eq( - JSON.stringify({ - title: "Hello", - message: "There", - }), - "currentEventPayload has correct value" - ); - }); -}); diff --git a/packages/core/tests/framework.spec.ts b/packages/core/tests/framework.spec.ts deleted file mode 100644 index 7d7d6bf0..00000000 --- a/packages/core/tests/framework.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Integration } from "../src"; - -describe("Custom Framework Tests", () => { - let boundFramework = false; - - const customIntegration = new Integration({ - name: "test2", - updateMethod: () => {}, - bind: () => { - return true; - }, - }); - - // Define Agile with framework - const App = new Agile(); - - /* - it('Has bound custom Framework', () => { - expect(boundFramework).to.eq(true, 'boundFramework has correct value'); - expect(App.integrations.integrations.size).to.eq(2, 'Integrations has correct size'); - expect(typeof App.integration?.bind === 'function').to.eq(true, 'Integration bind method get set'); - expect(typeof App.integration?.updateMethod === 'function').to.eq(true, 'Integration updateMethod method get set'); - expect(App.integration?.ready).to.eq(true, 'Integration is Ready'); - }); - */ -}); diff --git a/packages/core/tests/helper/test.integration.ts b/packages/core/tests/helper/test.integration.ts new file mode 100644 index 00000000..d783fcae --- /dev/null +++ b/packages/core/tests/helper/test.integration.ts @@ -0,0 +1,7 @@ +import { Agile, Integration } from "../../src"; + +const testIntegration = new Integration({ + key: "test", +}); + +export default testIntegration; diff --git a/packages/core/tests/integration/collection.persistent.integration.test.ts b/packages/core/tests/integration/collection.persistent.integration.test.ts new file mode 100644 index 00000000..cabfe49d --- /dev/null +++ b/packages/core/tests/integration/collection.persistent.integration.test.ts @@ -0,0 +1,226 @@ +// Note: This is no optimized Test! +// It was manly used to see if the collection persistent works during the development + +import { Agile, Item } from "../../src"; + +describe("Collection Persist Function Tests", () => { + const myStorage: any = {}; + const storageMethods = { + get: jest.fn((key) => { + // console.log(`GET '${key}'`); + return myStorage[key]; + }), + set: jest.fn((key, value) => { + // console.log(`SET '${key}'`, value); + myStorage[key] = value; + }), + remove: jest.fn((key) => { + // console.log(`DELETE '${key}'`); + delete myStorage[key]; + }), + }; + + // Define Agile with Storage + const App = new Agile(); + App.registerStorage( + App.Storage({ + key: "testStorage", + prefix: "test", + methods: storageMethods, + }) + ); + + interface User { + id: number; + name: string; + } + + describe("Collection", () => { + it("Can persist Collection", async () => { + // Create Collection + const MY_COLLECTION = App.Collection(); + + // Test Collecting Item before Persisting + MY_COLLECTION.collect({ id: 2, name: "hans" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({}); + expect(storageMethods.set).not.toHaveBeenCalled(); + expect(storageMethods.get).not.toHaveBeenCalled(); + expect(storageMethods.remove).not.toHaveBeenCalled(); + + // Test Persisting + MY_COLLECTION.persist("myCollection"); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_default: "[2]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(3); + expect(storageMethods.get).toHaveBeenCalledTimes(3); + expect(storageMethods.remove).toHaveBeenCalledTimes(0); + + // Test collecting new Item + MY_COLLECTION.collect({ id: 1, name: "frank" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_default: "[2,1]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_1: '{"id":1,"name":"frank"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(5); + expect(storageMethods.get).toHaveBeenCalledTimes(4); + expect(storageMethods.remove).toHaveBeenCalledTimes(0); + + // Test creating Group + MY_COLLECTION.createGroup("stupidPeople", [1, 2]).persist({ + followCollectionPersistKeyPattern: true, + }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_stupidPeople: "[1,2]", + _test__myCollection_group_default: "[2,1]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_1: '{"id":1,"name":"frank"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(6); + expect(storageMethods.get).toHaveBeenCalledTimes(5); + expect(storageMethods.remove).toHaveBeenCalledTimes(0); + + // Test collecting new Item + MY_COLLECTION.collect({ id: 3, name: "günter" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_stupidPeople: "[1,2]", + _test__myCollection_group_default: "[2,1,3]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_1: '{"id":1,"name":"frank"}', + _test__myCollection_item_3: '{"id":3,"name":"günter"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(8); + expect(storageMethods.get).toHaveBeenCalledTimes(6); + expect(storageMethods.remove).toHaveBeenCalledTimes(0); + + // Test updating Item + MY_COLLECTION.update(3, { name: "Benno" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_stupidPeople: "[1,2]", + _test__myCollection_group_default: "[2,1,3]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_1: '{"id":1,"name":"frank"}', + _test__myCollection_item_3: '{"id":3,"name":"Benno"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(9); + expect(storageMethods.get).toHaveBeenCalledTimes(6); + expect(storageMethods.remove).toHaveBeenCalledTimes(0); + + // Test updating Item with ItemKey + MY_COLLECTION.update(1, { id: 37, name: "Arne" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_stupidPeople: "[37,2]", + _test__myCollection_group_default: "[2,37,3]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_37: '{"id":37,"name":"Arne"}', + _test__myCollection_item_3: '{"id":3,"name":"Benno"}', + }); + expect(storageMethods.set).toHaveBeenCalledTimes(12); + expect(storageMethods.get).toHaveBeenCalledTimes(6); + expect(storageMethods.remove).toHaveBeenCalledTimes(1); + }); + + it("Can load persisted Collection", async () => { + // Create Collection + const MY_COLLECTION = App.Collection(); + + // Load persisted Value + MY_COLLECTION.persist("myCollection"); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_stupidPeople: "[37,2]", + _test__myCollection_group_default: "[2,37,3]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_item_37: '{"id":37,"name":"Arne"}', + _test__myCollection_item_3: '{"id":3,"name":"Benno"}', + }); + expect(MY_COLLECTION.isPersisted).toBeTruthy(); + expect(MY_COLLECTION.size).toBe(3); + expect(MY_COLLECTION.data["2"]).toBeInstanceOf(Item); + expect(MY_COLLECTION.data["37"]).toBeInstanceOf(Item); + expect(MY_COLLECTION.data["3"]).toBeInstanceOf(Item); + + // Updating some Collection Stuff + MY_COLLECTION.update(3, { name: "Angela" }); + MY_COLLECTION.collect({ id: 4, name: "Paul" }); + MY_COLLECTION.collect({ id: 99, name: "Jeff" }); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_default: "[2,37,3,4,99]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_group_stupidPeople: "[37,2]", + _test__myCollection_item_3: '{"id":3,"name":"Angela"}', + _test__myCollection_item_37: '{"id":37,"name":"Arne"}', + _test__myCollection_item_4: '{"id":4,"name":"Paul"}', + _test__myCollection_item_99: '{"id":99,"name":"Jeff"}', + }); + + // Test removing Item + MY_COLLECTION.remove(3).everywhere(); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_default: "[2,37,4,99]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_group_stupidPeople: "[37,2]", + _test__myCollection_item_37: '{"id":37,"name":"Arne"}', + _test__myCollection_item_4: '{"id":4,"name":"Paul"}', + _test__myCollection_item_99: '{"id":99,"name":"Jeff"}', + }); + }); + + it("Can remove persisted Collection", async () => { + // Create Collection + const MY_COLLECTION = App.Collection(); + + // Load persisted Value + MY_COLLECTION.persist("myCollection"); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test_myCollection: "true", + _test__myCollection_group_default: "[2,37,4,99]", + _test__myCollection_item_2: '{"id":2,"name":"hans"}', + _test__myCollection_group_stupidPeople: "[37,2]", + _test__myCollection_item_37: '{"id":37,"name":"Arne"}', + _test__myCollection_item_4: '{"id":4,"name":"Paul"}', + _test__myCollection_item_99: '{"id":99,"name":"Jeff"}', + }); + + // Test Removing Persisted Value + MY_COLLECTION.persistent?.removePersistedValue(); + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(myStorage).toStrictEqual({ + _test__myCollection_group_stupidPeople: "[37,2]", + }); + }); + }); +}); diff --git a/packages/core/tests/logger.spec.ts b/packages/core/tests/logger.spec.ts deleted file mode 100644 index 5b84dfcb..00000000 --- a/packages/core/tests/logger.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Logger } from "../src"; - -describe("Logger tests", () => { - const logger = new Logger(); - - it("Does Log", () => { - logger.trace("This is a Trace"); - logger.debug("this is a test"); - logger.debug("this is a test", ["ello"]); - logger.info("this is a Test", {}, {}); - logger.warn("This is a Warning"); - logger.table("This is a Table", { test: "test" }); - logger.error("This is an Error"); - logger.if.tag(["test"]).log("faranke"); - }); -}); diff --git a/packages/core/tests/state/default.spec.ts b/packages/core/tests/state/default.spec.ts deleted file mode 100644 index 9f7827a8..00000000 --- a/packages/core/tests/state/default.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../src"; - -describe("Default State Tests", () => { - // Define Agile - const App = new Agile(); - - describe("State", () => { - // Create State - const MY_STATE = App.State("hello"); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq("hello", "MY_STATE has correct value"); - expect(typeof MY_STATE.value === "string").to.eq( - true, - "MY_STATE has correct type" - ); - expect(MY_STATE._value).to.eq("hello", "MY_STATE has correct _value"); - expect(MY_STATE.previousStateValue).to.eq( - "hello", - "MY_STATE has correct previousState" - ); - expect(MY_STATE.key).to.eq(undefined, "MY_STATE has correct key"); - expect(MY_STATE._key).to.eq(undefined, "My_STATE has correct _key"); - expect(JSON.stringify(MY_STATE.sideEffects)).to.eq( - JSON.stringify({}), - "MY_STATE has no sideEffects" - ); - expect(MY_STATE.nextStateValue).to.eq( - "hello", - "MY_STATE has correct nextState" - ); - expect(MY_STATE.initialStateValue).to.eq( - "hello", - "MY_STATE has correct initialState" - ); - expect(MY_STATE.exists).to.eq(true, "MY_STATE has correct exists"); - expect(MY_STATE.isSet).to.eq(false, "MY_STATE has correct isSet"); - expect(MY_STATE.isPersisted).to.eq( - false, - "MY_STATE has correct isPersisted" - ); - expect(MY_STATE.persistent).to.eq( - undefined, - "MY_STATE has no persistManager" - ); - expect(MY_STATE.isPlaceholder).to.eq( - false, - "MY_STATE has correct isPlaceholder" - ); - expect(MY_STATE.valueType).to.eq( - undefined, - "MY_STATE has correct valueType" - ); - expect(MY_STATE.exists).to.eq(true, "MY_STATE exists"); - }); - - it("Can change key", () => { - // Update key - MY_STATE.key = "myNewKey"; - - expect(MY_STATE.key).to.eq("myNewKey", "MY_STATE has correct key"); - expect(MY_STATE._key).to.eq("myNewKey", "My_STATE has correct _key"); - }); - - it("Can change value", () => { - // Update value - MY_STATE.value = "bye"; - - expect(MY_STATE.value).to.eq("bye", "MY_STATE has correct value"); - expect(MY_STATE._value).to.eq("bye", "My_STATE has correct _value"); - }); - }); - - describe("State with Key", () => { - // Create State - const MY_STATE_WITH_KEY = App.State(true, "myKey"); - - it("Has correct initial values", () => { - expect(typeof MY_STATE_WITH_KEY.value === "boolean").to.eq( - true, - "MY_STATE_WITH_KEY has correct type" - ); - expect(MY_STATE_WITH_KEY.key).to.eq( - "myKey", - "MY_STATE_WITH_KEY has correct key" - ); - expect(MY_STATE_WITH_KEY._key).to.eq( - "myKey", - "MY_STATE_WITH_KEY has correct _key" - ); - }); - - it("Can change key", () => { - // Update key - MY_STATE_WITH_KEY.key = "myNewKey"; - - expect(MY_STATE_WITH_KEY.key).to.eq( - "myNewKey", - "MY_STATE_WITH_KEY has correct key" - ); - expect(MY_STATE_WITH_KEY._key).to.eq( - "myNewKey", - "MY_STATE_WITH_KEY has correct _key" - ); - }); - - it("Can change value", () => { - // Update value - MY_STATE_WITH_KEY.value = false; - - expect(MY_STATE_WITH_KEY.value).to.eq( - false, - "MY_STATE_WITH_KEY has correct value" - ); - expect(MY_STATE_WITH_KEY._value).to.eq( - false, - "MY_STATE_WITH_KEY has correct _value" - ); - }); - }); -}); diff --git a/packages/core/tests/state/functions/onInaugurated.function.spec.ts b/packages/core/tests/state/functions/onInaugurated.function.spec.ts deleted file mode 100644 index 8a14cc60..00000000 --- a/packages/core/tests/state/functions/onInaugurated.function.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("OnInaugurated Function Tests", () => { - let calledWatcherCount = 0; - let watcherValue = 1; - - // Define Agile - const App = new Agile(); - - // Create State - const MY_STATE = App.State(1); - - // Create Watcher - MY_STATE.onInaugurated((value) => { - watcherValue = value; - calledWatcherCount++; - }); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.watchers.InauguratedWatcher !== undefined).to.eq( - true, - "MY_STATE has Inaugurated in watchers" - ); - }); - - it("Can Watch State once", async () => { - // Update State - MY_STATE.set(2); - - // Needs some time to call watcher - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - expect(calledWatcherCount).to.eq( - 1, - "calledWatcherCount has been increased by 1" - ); - expect(watcherValue).to.eq(2, "watcherValue has correct value"); - expect(MY_STATE.watchers.InauguratedWatcher !== undefined).to.eq( - false, - "Inaugurated Watcher got removed/destroyed" - ); - }); -}); diff --git a/packages/core/tests/state/functions/onLoad.function.spec.ts b/packages/core/tests/state/functions/onLoad.function.spec.ts deleted file mode 100644 index ef8f353f..00000000 --- a/packages/core/tests/state/functions/onLoad.function.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Agile } from "../../../src"; -import { expect } from "chai"; - -describe("OnLoad Function Tests", () => { - const myStorage: any = {}; - let calledOnLoadCount = 0; - - // Define Agile with Storage - const App = new Agile(); - App.registerStorage( - App.Storage({ - key: "testStorage", - prefix: "test", - methods: { - get: (key) => { - return myStorage[key]; - }, - set: (key, value) => { - myStorage[key] = value; - }, - remove: (key) => { - delete myStorage[key]; - }, - }, - }), - { default: false } - ); - - // Create State - const MY_STATE = App.State(1); - - it("Has correct initial values", () => { - expect(MY_STATE.isPersisted).to.eq( - false, - "MY_STATE has correct isPersisted" - ); - - expect(calledOnLoadCount).to.eq(0, "calledOnLoadCount has correct Value"); - }); - - it("Can't register onLoad before persisting Value", async () => { - MY_STATE.onLoad(() => { - calledOnLoadCount++; - }); - MY_STATE.persist("myState"); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.isPersisted).to.eq(true, "MY_STATE is persisted"); - - expect(calledOnLoadCount).to.eq(0, "calledOnLoadCount has correct Value"); - }); - - it("Can register onLoad after persisting Value", async () => { - MY_STATE.onLoad(() => { - calledOnLoadCount++; - }); - - expect(MY_STATE.isPersisted).to.eq(true, "MY_STATE is persisted"); - - expect(calledOnLoadCount).to.eq(1, "calledOnLoadCount got increased by 1"); - }); -}); diff --git a/packages/core/tests/state/functions/patch.function.spec.ts b/packages/core/tests/state/functions/patch.function.spec.ts deleted file mode 100644 index aedc302c..00000000 --- a/packages/core/tests/state/functions/patch.function.spec.ts +++ /dev/null @@ -1,266 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Patch Function Tests", () => { - let rerenderCount = 0; - let sideEffectCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Object Interface - interface userInterface { - id: number; - name: string; - } - - // Create State - const MY_STATE = App.State({ id: 1, name: "jeff" }); - - // Set sideEffects for testing the functionality of it - MY_STATE.addSideEffect("test", () => { - sideEffectCount++; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_STATE.observer]); - - it("Has correct initial values", () => { - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_STATE has correct value" - ); - expect(MY_STATE.observer.subs.size === 1).to.eq( - true, - "MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)" - ); - expect(typeof MY_STATE.sideEffects["test"] === "function").to.eq( - true, - "MY_STATE has sideEffect function" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount is 0"); - expect(sideEffectCount).to.eq(0, "sideEffectCount is 0"); - }); - - describe("Patch State", () => { - it("Can patch value into State", async () => { - // Patch Value - MY_STATE.patch({ name: "hans" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE has correct value" - ); - expect(JSON.stringify(MY_STATE.previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_STATE has correct previousState" - ); - expect(JSON.stringify(MY_STATE.nextStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE has correct nextState" - ); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq( - 1, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - - it("Can't patch value into State if the value is the same", async () => { - // Patch Value - MY_STATE.patch({ name: "hans" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE value stayed the same" - ); - expect(JSON.stringify(MY_STATE.previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_STATE previousState stayed the same" - ); - expect(JSON.stringify(MY_STATE.nextStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE nextState stayed the same" - ); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE isSet stayed the same"); - - expect(sideEffectCount).to.eq(1, "sideEffectCount hasn't been increased"); - expect(rerenderCount).to.eq(1, "rerenderCount hasn't been increased"); - }); - - it("Can't patch value into State which is no Object", async () => { - // Patch State - // @ts-ignore - MY_STATE.patch("noObject"); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE value stayed the same" - ); - expect(JSON.stringify(MY_STATE.previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "jeff", - }), - "MY_STATE previousState stayed the same" - ); - expect(JSON.stringify(MY_STATE.nextStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE nextState stayed the same" - ); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE isSet stayed the same"); - - expect(sideEffectCount).to.eq(1, "sideEffectCount hasn't been increased"); - expect(rerenderCount).to.eq(1, "rerenderCount hasn't been increased"); - }); - - it("Can't patch value into non Object State", async () => { - // Create State - const MY_NON_OBJECT_STATE = App.State("test"); - - // Patch State - MY_NON_OBJECT_STATE.patch({ test: "test" }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_NON_OBJECT_STATE.value).to.eq( - "test", - "MY_NON_OBJECT_STATE value stayed the same" - ); - expect(MY_NON_OBJECT_STATE.previousStateValue).to.eq( - "test", - "MY_NON_OBJECT_STATE previousState stayed the same" - ); - expect(MY_NON_OBJECT_STATE.nextStateValue).to.eq( - "test", - "MY_NON_OBJECT_STATE nextState stayed the same" - ); - expect(MY_NON_OBJECT_STATE.isSet).to.eq( - false, - "MY_NON_OBJECT_STATE isSet stayed the same" - ); - }); - }); - - describe("Test addNewProperties option", () => { - it("Doesn't add property to value with addNewProperties = false", async () => { - // Patch State - MY_STATE.patch({ name: "frank", age: 10 }, { addNewProperties: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "frank", - }), - "MY_STATE has correct value" - ); - expect(JSON.stringify(MY_STATE.previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "hans", - }), - "MY_STATE has correct previousState" - ); - expect(JSON.stringify(MY_STATE.nextStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "frank", - }), - "MY_STATE has correct nextState" - ); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq( - 2, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Does add property to value with addNewProperties = true", async () => { - // Patch State - MY_STATE.patch({ name: "benno", age: 15 }, { addNewProperties: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(JSON.stringify(MY_STATE.value)).to.eq( - JSON.stringify({ - id: 1, - name: "benno", - age: 15, - }), - "MY_STATE has correct value" - ); - expect(JSON.stringify(MY_STATE.previousStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "frank", - }), - "MY_STATE has correct previousState" - ); - expect(JSON.stringify(MY_STATE.nextStateValue)).to.eq( - JSON.stringify({ - id: 1, - name: "benno", - age: 15, - }), - "MY_STATE has correct nextState" - ); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq( - 3, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - }); -}); diff --git a/packages/core/tests/state/functions/persist.function.spec.ts b/packages/core/tests/state/functions/persist.function.spec.ts deleted file mode 100644 index 7cf0dd3e..00000000 --- a/packages/core/tests/state/functions/persist.function.spec.ts +++ /dev/null @@ -1,278 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile, Persistent } from "../../../src"; - -describe("Persist Function Tests", () => { - const myStorage: any = {}; - - // Define Agile with Storage - const App = new Agile(); - App.registerStorage( - App.Storage({ - key: "testStorage", - prefix: "test", - methods: { - get: (key) => { - return myStorage[key]; - }, - set: (key, value) => { - myStorage[key] = value; - }, - remove: (key) => { - delete myStorage[key]; - }, - }, - }), - { default: true } - ); - - describe("State", () => { - // Create State - const MY_STATE = App.State(1); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.isPersisted).to.eq( - false, - "MY_STATE has correct isPersisted" - ); - expect(MY_STATE.persistent).to.eq( - undefined, - "MY_STATE has no persistManager" - ); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(false, "MY_STATE isn't in persistedStates"); - }); - - it("Can't persist State without persist Key", () => { - // Persist State - MY_STATE.persist(); - - expect(MY_STATE.isPersisted).to.eq( - false, - "MY_STATE has correct isPersisted" - ); - expect(MY_STATE.persistent !== undefined).to.eq( - true, - "MY_STATE has persistManager" - ); - expect(MY_STATE.persistent?.ready).to.eq( - false, - "MY_STATE persistManager is not ready" - ); - expect(MY_STATE.persistent?.key).to.eq( - "unknown", - "MY_STATE persistManager has 'unknown' key" - ); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(false, "MY_STATE isn't in persistedStates"); - expect(MY_STATE.key).to.eq(undefined, "MY_STATE has correct key"); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(false, "MY_STATE isn't in persistedStates"); - expect(App.storages.get("mySecondKey")).to.eq( - undefined, - "MY_STATE isn't in storage" - ); - }); - - it("Can persist State with persist Key", async () => { - // Persist State - MY_STATE.persist("mySecondKey"); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.isPersisted).to.eq( - true, - "MY_STATE has correct isPersisted" - ); - expect(MY_STATE.persistent !== undefined).to.eq( - true, - "MY_STATE has persistManager" - ); - expect(MY_STATE.persistent?.key).to.eq( - "mySecondKey", - "MY_STATE persistManager has correct Key" - ); - expect(MY_STATE.persistent?.ready).to.eq( - true, - "MY_STATE persistManager is ready" - ); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(true, "MY_STATE is in persistedStates"); - expect(MY_STATE.key).to.eq( - "mySecondKey", - "MY_STATE key has been set to persistKey if no key is provided" - ); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(true, "MY_STATE isn't in persistedStates"); - expect(App.storages.get("mySecondKey")).to.eq( - 1, - "MY_STATE is in storage" - ); - }); - - describe("Test reset method on persist State", () => { - it("Removes the State from the Storage if it get reset", () => { - // Reset State - MY_STATE.reset(); - - expect(MY_STATE.isPersisted).to.eq( - true, - "MY_STATE has correct isPersisted" - ); - expect(MY_STATE.persistent?.isPersisted).to.eq( - false, - "MY_STATE persistent has correct isPersisted" - ); - expect(MY_STATE.key).to.eq("mySecondKey", "MY_STATE has correct key"); - expect( - App.storages.persistentInstances.has( - MY_STATE.persistent as Persistent - ) - ).to.eq(true, "MY_STATE is in persistedStates"); - expect(App.storages.get("mySecondKey")).to.eq( - undefined, - "MY_STATE isn't in storage" - ); - }); - }); - - describe("Test set method on persist State", () => { - it("Updates State in the Storage if it get changed", () => { - console.log(MY_STATE); - // Reset State - MY_STATE.set(5); - - expect(MY_STATE.isPersisted).to.eq( - true, - "MY_STATE has correct isPersisted" - ); - expect( - MY_STATE.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE.persistent) - ).to.eq(true, "MY_STATE is in persistedStates"); - expect(App.storages.get("mySecondKey")).to.eq( - 5, - "MY_STATE_WITH_KEY is in storage and has been updated" - ); - }); - }); - }); - - describe("State with Key", () => { - // Create State - const MY_STATE_WITH_KEY = App.State("hello", "myKey"); - - it("Has correct initial values", () => { - expect(MY_STATE_WITH_KEY.value).to.eq( - "hello", - "MY_STATE_WITH_KEY has correct value" - ); - expect(MY_STATE_WITH_KEY.key).to.eq( - "myKey", - "MY_STATE_WITH_KEY has correct key" - ); - expect(MY_STATE_WITH_KEY.isPersisted).to.eq( - false, - "MY_STATE_WITH_KEY has correct isPersistState" - ); - expect(MY_STATE_WITH_KEY.persistent).to.eq( - undefined, - "MY_STATE_WITH_KEY has no persistManager" - ); - expect( - MY_STATE_WITH_KEY.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE_WITH_KEY.persistent) - ).to.eq(false, "MY_STATE_WITH_KEY isn't in persistedStates"); - expect(App.storages.get("myKey")).to.eq( - undefined, - "MY_STATE_WITH_KEY isn't in storage" - ); - }); - - it("Can persist State without persist Key", async () => { - // Persist State - MY_STATE_WITH_KEY.persist(); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE_WITH_KEY.isPersisted).to.eq( - true, - "MY_STATE_WITH_KEY has correct isPersistState" - ); - expect(MY_STATE_WITH_KEY.persistent !== undefined).to.eq( - true, - "MY_STATE_WITH_KEY has persistManager" - ); - expect(MY_STATE_WITH_KEY.persistent?.key).to.eq( - "myKey", - "MY_STATE_WITH_KEY persistManager has correct key" - ); - expect(MY_STATE_WITH_KEY.persistent?.ready).to.eq( - true, - "MY_STATE_WITH_KEY persistManager is ready" - ); - expect( - MY_STATE_WITH_KEY.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE_WITH_KEY.persistent) - ).to.eq(true, "MY_STATE_WITH_KEY is in persistedStates"); - expect(App.storages.get("myKey")).to.eq( - "hello", - "MY_STATE_WITH_KEY is in storage" - ); - }); - - it("Can persist State with persist Key", async () => { - // Persist State - MY_STATE_WITH_KEY.persist("myThirdKey"); - - // Needs some time to persist value - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE_WITH_KEY.isPersisted).to.eq( - true, - "MY_STATE_WITH_KEY has correct isPersistState" - ); - expect(MY_STATE_WITH_KEY.persistent !== undefined).to.eq( - true, - "MY_STATE_WITH_KEY has persistManager" - ); - expect(MY_STATE_WITH_KEY.persistent?.key).to.eq( - "myThirdKey", - "MY_STATE_WITH_KEY persistManager has correct key" - ); - expect(MY_STATE_WITH_KEY.persistent?.ready).to.eq( - true, - "MY_STATE_WITH_KEY persistManager is ready" - ); - expect(MY_STATE_WITH_KEY.key).to.eq( - "myKey", - "MY_STATE_WITH_KEY has correct key" - ); - expect( - MY_STATE_WITH_KEY.persistent !== undefined && - App.storages.persistentInstances.has(MY_STATE_WITH_KEY.persistent) - ).to.eq(true, "MY_STATE_WITH_KEY is in persistedStates"); - expect(App.storages.get("myThirdKey")).to.eq( - "hello", - "MY_STATE_WITH_KEY with new key is in storage" - ); - expect(App.storages.get("myKey")).to.eq( - undefined, - "MY_STATE_WITH_KEY with old key isn't in storage" - ); - }); - }); -}); diff --git a/packages/core/tests/state/functions/reset.function.spec.ts b/packages/core/tests/state/functions/reset.function.spec.ts deleted file mode 100644 index 1d692dde..00000000 --- a/packages/core/tests/state/functions/reset.function.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Reset Function Tests", () => { - let rerenderCount = 0; - let sideEffectCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Create State - const MY_STATE = App.State(1); - - // Set sideEffects for testing the functionality of it - MY_STATE.addSideEffect("test", () => { - sideEffectCount++; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_STATE.observer]); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.observer.subs.size === 1).to.eq( - true, - "MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)" - ); - expect(typeof MY_STATE.sideEffects["test"] === "function").to.eq( - true, - "MY_STATE has sideEffect function" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount is 0"); - expect(sideEffectCount).to.eq(0, "sideEffectCount is 0"); - }); - - it("Can reset State", async () => { - // Change State - MY_STATE.set(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Change State - MY_STATE.set(5); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Reset State - MY_STATE.reset(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.previousStateValue).to.eq( - 5, - "MY_STATE has correct previousState" - ); - expect(MY_STATE.nextStateValue).to.eq(1, "MY_STATE has correct nextState"); - expect(MY_STATE.isSet).to.eq(false, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq( - 3, - "sideEffectCount has been increased by 3 (2 by set, 1 by reset)" - ); - expect(rerenderCount).to.eq( - 3, - "rerenderCount has been increased by 3 (2 by set, 1 by reset)" - ); - }); -}); diff --git a/packages/core/tests/state/functions/set.function.spec.ts b/packages/core/tests/state/functions/set.function.spec.ts deleted file mode 100644 index 6e5d525f..00000000 --- a/packages/core/tests/state/functions/set.function.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Set Function Tests", () => { - let rerenderCount = 0; - let sideEffectCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Create State - const MY_STATE = App.State(1); - - // Set sideEffects for testing the functionality of it - MY_STATE.addSideEffect("test", () => { - sideEffectCount++; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_STATE.observer]); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.observer.subs.size === 1).to.eq( - true, - "MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)" - ); - expect(typeof MY_STATE.sideEffects["test"] === "function").to.eq( - true, - "MY_STATE has sideEffect function" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount is 0"); - expect(sideEffectCount).to.eq(0, "sideEffectCount is 0"); - }); - - it("Can change State", async () => { - // Change State - MY_STATE.set(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - expect(MY_STATE.previousStateValue).to.eq( - 1, - "MY_STATE has correct previousState" - ); - expect(MY_STATE.nextStateValue).to.eq(2, "MY_STATE has correct nextState"); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - expect(MY_STATE.exists).to.eq(true, "MY_STATE exists"); - - expect(sideEffectCount).to.eq(1, "sideEffectCount has been increased by 1"); - expect(rerenderCount).to.eq(1, "rerenderCount has been increased by 1"); - }); - - it("Can't change State with the same value", async () => { - // Change State - MY_STATE.set(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - expect(MY_STATE.previousStateValue).to.eq( - 1, - "MY_STATE has correct previousState" - ); - expect(MY_STATE.nextStateValue).to.eq(2, "MY_STATE has correct nextState"); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq(1, "sideEffectCount hasn't been increased"); - expect(rerenderCount).to.eq(1, "rerenderCount hasn't been increased"); - }); - - describe("Test sideEffects option", () => { - it("Does call sideEffects by changing State with sideEffects = true", async () => { - // Change State - MY_STATE.set(3, { sideEffects: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(sideEffectCount).to.eq( - 2, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(2, "rerenderCount has been increased by 1"); - }); - - it("Doesn't call sideEffects by changing State with sideEffects = false", async () => { - // Change State - MY_STATE.set(4, { sideEffects: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(sideEffectCount).to.eq(2, "sideEffectCount hasn't been increased"); - expect(rerenderCount).to.eq(3, "rerenderCount has been increased by 1"); - }); - }); - - describe("Test background option", () => { - it("Doesn't call callBackFunction by changing State with background = true", async () => { - // Change State - MY_STATE.set(5, { background: true }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(sideEffectCount).to.eq( - 3, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(3, "rerenderCount hasn't been increased"); - }); - - it("Does call callBackFunction by changing State with background = false", async () => { - // Change State - MY_STATE.set(6, { background: false }); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(sideEffectCount).to.eq( - 4, - "sideEffectCount has been increased by 1" - ); - expect(rerenderCount).to.eq(4, "rerenderCount has been increased by 1"); - }); - }); -}); diff --git a/packages/core/tests/state/functions/type.function.spec.ts b/packages/core/tests/state/functions/type.function.spec.ts deleted file mode 100644 index d866e1e5..00000000 --- a/packages/core/tests/state/functions/type.function.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Type Function Tests", () => { - // Define Agile - const App = new Agile(); - - // Create State - const MY_STATE = App.State(1).type(Number); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(typeof MY_STATE.value === "number").to.eq( - true, - "MY_STATE has correct type" - ); - expect(MY_STATE.valueType).to.eq("number", "MY_STATE correct valueType"); - }); - - it("Can change State with correct Type", async () => { - // Change State - MY_STATE.set(2); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - }); - - it("Can't change State with wrong Type", async () => { - // Change State - // @ts-ignore - MY_STATE.set("Hello"); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - }); -}); diff --git a/packages/core/tests/state/functions/undo.function.spec.ts b/packages/core/tests/state/functions/undo.function.spec.ts deleted file mode 100644 index 8d67f072..00000000 --- a/packages/core/tests/state/functions/undo.function.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; -import testIntegration from "../../test.integration"; - -describe("Undo Function Tests", () => { - let rerenderCount = 0; - let sideEffectCount = 0; - - // Define Agile - const App = new Agile().use(testIntegration); - - // Create State - const MY_STATE = App.State(1); - - // Set sideEffects for testing the functionality of it - MY_STATE.addSideEffect("test", () => { - sideEffectCount++; - }); - - // Subscribe Instance for testing callback call functionality - App.subController.subscribeWithSubsArray(() => { - rerenderCount++; - }, [MY_STATE.observer]); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.observer.subs.size === 1).to.eq( - true, - "MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)" - ); - expect(typeof MY_STATE.sideEffects["test"] === "function").to.eq( - true, - "MY_STATE has sideEffect function" - ); - - expect(rerenderCount).to.eq(0, "rerenderCount is 0"); - expect(sideEffectCount).to.eq(0, "sideEffectCount is 0"); - }); - - it("Can undo State", async () => { - // Change State - MY_STATE.set(2); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Change State - MY_STATE.set(5); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Undo State - MY_STATE.undo(); - - // Needs some time to call callbackFunction - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - expect(MY_STATE.previousStateValue).to.eq( - 5, - "MY_STATE has correct previousState" - ); - expect(MY_STATE.nextStateValue).to.eq(2, "MY_STATE has correct nextState"); - expect(MY_STATE.isSet).to.eq(true, "MY_STATE has correct isSet"); - - expect(sideEffectCount).to.eq( - 3, - "sideEffectCount has been increased by 3 (2 by set, 1 by undo)" - ); - expect(rerenderCount).to.eq( - 3, - "rerenderCount has been increased by 3 (2 by set, 1 by undo)" - ); - }); -}); diff --git a/packages/core/tests/state/functions/watch.function.spec.ts b/packages/core/tests/state/functions/watch.function.spec.ts deleted file mode 100644 index cac5be7a..00000000 --- a/packages/core/tests/state/functions/watch.function.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../../../src"; - -describe("Watcher Tests", () => { - let calledWatcherCount = 0; - let watcherValue = 1; - - // Define Agile - const App = new Agile(); - - // Create State - const MY_STATE = App.State(1); - - // Create Watcher - MY_STATE.watch("firstWatcher", (value) => { - watcherValue = value; - calledWatcherCount++; - }); - - it("Has correct initial values", () => { - expect(MY_STATE.value).to.eq(1, "MY_STATE has correct value"); - expect(MY_STATE.watchers.firstWatcher !== undefined).to.eq( - true, - "MY_STATE has firstWatcher in watchers" - ); - }); - - it("Can Watch State", async () => { - // Update State - MY_STATE.set(2); - - // Needs some time to call watcher - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(2, "MY_STATE has correct value"); - expect(calledWatcherCount).to.eq( - 1, - "calledWatcherCount has been increased by 1" - ); - expect(watcherValue).to.eq(2, "watcherValue has correct value"); - }); - - it("Can Remove Watcher", async () => { - // Remove Watcher - MY_STATE.removeWatcher("firstWatcher"); - - // Update State - MY_STATE.set(3); - - // Needs some time to call watcher - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(MY_STATE.value).to.eq(3, "MY_STATE has correct value"); - expect(calledWatcherCount).to.eq( - 1, - "calledWatcherCount hasn't been increased" - ); - expect(watcherValue).to.eq( - 2, - "watcherValue has't change.. because of no update" - ); - }); -}); diff --git a/packages/core/tests/storage.spec.ts b/packages/core/tests/storage.spec.ts deleted file mode 100644 index 68892b48..00000000 --- a/packages/core/tests/storage.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import { Agile } from "../src"; - -describe("Custom Storage Tests", () => { - const myStorage: any = {}; - - // Define Agile - const App = new Agile(); - - // Create Storage - const storage = App.Storage({ - key: "testStorage", - prefix: "test", - methods: { - get: (key) => { - return myStorage[key]; - }, - set: (key, value) => { - myStorage[key] = value; - }, - remove: (key) => { - delete myStorage[key]; - }, - }, - }); - - describe("Can work with numbers", () => { - it("Can set an item into the Storage", async () => { - // Set Item into Storage - storage.set("myKey", 1); - - expect(myStorage["_test_myKey"]).to.eq(JSON.stringify(1)); - }); - - it("Can get an item from the Storage", () => { - // Get Item from Storage - const item = storage.get("myKey"); - - expect(item).to.eq(1); - }); - - it("Can remove an item from the Storage", () => { - // Remove Item from Storage - storage.remove("myKey"); - - expect(myStorage["_test_myKey"]).to.eq(undefined); - expect(storage.get("myKey")).to.eq(undefined); - }); - }); - - describe("Can work with object", () => { - it("Can set an item into the Storage", () => { - // Set Item into Storage - storage.set("mySecondKey", { id: 1, name: "jeff" }); - - expect(myStorage["_test_mySecondKey"]).to.eq( - JSON.stringify({ id: 1, name: "jeff" }) - ); - }); - - it("Can get an item from the Storage", () => { - // Get Item from Storage - const item = storage.get("mySecondKey"); - - expect(JSON.stringify(item)).to.eq( - JSON.stringify({ id: 1, name: "jeff" }) - ); - }); - - it("Can remove an item from the Storage", () => { - // Remove Item from Storage - storage.remove("mySecondKey"); - - expect(myStorage["_test_mySecondKey"]).to.eq(undefined); - expect(storage.get("mySecondKey")).to.eq(undefined); - }); - }); - - describe("Can work with array", () => { - it("Can set an item into the Storage", () => { - // Set Item into Storage - storage.set("myThirdKey", [1, 2, 3]); - - expect(myStorage["_test_myThirdKey"]).to.eq(JSON.stringify([1, 2, 3])); - }); - - it("Can get an item from the Storage", () => { - // Get Item from Storage - const item = storage.get("myThirdKey"); - - expect(JSON.stringify(item)).to.eq(JSON.stringify([1, 2, 3])); - }); - - it("Can remove an item from the Storage", () => { - // Remove Item from Storage - storage.remove("myThirdKey"); - - expect(myStorage["myThirdKey"]).to.eq(undefined); - expect(storage.get("myThirdKey")).to.eq(undefined); - }); - }); -}); diff --git a/packages/core/tests/test.integration.ts b/packages/core/tests/test.integration.ts deleted file mode 100644 index e7941af7..00000000 --- a/packages/core/tests/test.integration.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Agile, Integration } from "../src"; - -const testIntegration = new Integration({ - name: "test", - frameworkInstance: null, - bind(agileInstance: Agile) { - // Nothing to bind ;D - return true; - }, - updateMethod(componentInstance: any, updatedData: Object) { - // Nothing - }, -}); - -export default testIntegration; diff --git a/packages/core/tests/unit/agile.test.ts b/packages/core/tests/unit/agile.test.ts new file mode 100644 index 00000000..c95fb200 --- /dev/null +++ b/packages/core/tests/unit/agile.test.ts @@ -0,0 +1,299 @@ +import { + Agile, + State, + Runtime, + SubController, + Integrations, + Storage, + Event, + Computed, + Collection, + Logger, + Storages, +} from "../../src"; +import testIntegration from "../helper/test.integration"; + +jest.mock("../../src/runtime/index"); +jest.mock("../../src/runtime/subscription/sub.controller"); +jest.mock("../../src/storages/index"); +jest.mock("../../src/integrations/index"); +jest.mock("../../src/storages/storage"); +jest.mock("../../src/collection/index"); +jest.mock("../../src/computed/index"); +jest.mock("../../src/event/index"); +/* Can't mock Logger because I somehow can't overwrite a static get method +jest.mock("../../src/logger/index", () => { + return class { + static get level() { + return { + TRACE: 1, + DEBUG: 2, + LOG: 5, + TABLE: 5, + INFO: 10, + WARN: 20, + ERROR: 50, + }; + } + }; +}); + */ +// jest.mock("../../src/state/index"); // Can't mock State because mocks get instantiated before everything else -> I got the good old not loaded Object error https://github.com/kentcdodds/how-jest-mocking-works + +describe("Agile Tests", () => { + const RuntimeMock = Runtime as jest.MockedClass; + const SubControllerMock = SubController as jest.MockedClass< + typeof SubController + >; + const StoragesMock = Storages as jest.MockedClass; + const IntegrationsMock = Integrations as jest.MockedClass< + typeof Integrations + >; + + beforeEach(() => { + RuntimeMock.mockClear(); + SubControllerMock.mockClear(); + StoragesMock.mockClear(); + IntegrationsMock.mockClear(); + + // Reset Global This + globalThis["__agile__"] = undefined; + }); + + it("should instantiate Agile (default config)", () => { + const agile = new Agile(); + + // Check if Agile properties got instantiated properly + expect(agile.config).toStrictEqual({ + waitForMount: false, + }); + expect(IntegrationsMock).toHaveBeenCalledWith(agile); + expect(agile.integrations).toBeInstanceOf(Integrations); + expect(RuntimeMock).toHaveBeenCalledWith(agile); + expect(agile.runtime).toBeInstanceOf(Runtime); + expect(SubControllerMock).toHaveBeenCalledWith(agile); + expect(agile.subController).toBeInstanceOf(SubController); + expect(StoragesMock).toHaveBeenCalledWith(agile, { + localStorage: true, + }); + expect(agile.storages).toBeInstanceOf(Storages); + + // Check if Static Logger has correct config + expect(Agile.logger.config).toStrictEqual({ + prefix: "Agile", + level: Logger.level.WARN, + canUseCustomStyles: true, + }); + expect(Agile.logger.allowedTags).toStrictEqual([ + "runtime", + "storage", + "subscription", + "multieditor", + ]); + expect(Agile.logger.isActive).toBeTruthy(); + + // Check if global Agile Instance got created + expect(globalThis["__agile__"]).toBe(agile); + }); + + it("should instantiate Agile with specific config", () => { + const agile = new Agile({ + waitForMount: true, + localStorage: false, + logConfig: { + level: Logger.level.DEBUG, + active: false, + prefix: "Jeff", + }, + }); + + // Check if Agile properties got instantiated properly + expect(agile.config).toStrictEqual({ + waitForMount: true, + }); + expect(IntegrationsMock).toHaveBeenCalledWith(agile); + expect(agile.integrations).toBeInstanceOf(Integrations); + expect(RuntimeMock).toHaveBeenCalledWith(agile); + expect(agile.runtime).toBeInstanceOf(Runtime); + expect(SubControllerMock).toHaveBeenCalledWith(agile); + expect(agile.subController).toBeInstanceOf(SubController); + expect(StoragesMock).toHaveBeenCalledWith(agile, { + localStorage: false, + }); + expect(agile.storages).toBeInstanceOf(Storages); + + // Check if Static Logger has correct config + expect(Agile.logger.config).toStrictEqual({ + prefix: "Jeff", + level: Logger.level.DEBUG, + canUseCustomStyles: true, + }); + expect(Agile.logger.allowedTags).toStrictEqual([ + "runtime", + "storage", + "subscription", + "multieditor", + ]); + expect(Agile.logger.isActive).toBeFalsy(); + + // Check if global Agile Instance got created + expect(globalThis["__agile__"]).toBe(agile); + }); + + describe("Agile Function Tests", () => { + let agile: Agile; + + beforeEach(() => { + agile = new Agile(); + }); + + describe("storage function tests", () => { + const StorageMock = Storage as jest.MockedClass; + + beforeEach(() => { + StorageMock.mockClear(); + }); + + it("should create Storage", () => { + const storageConfig = { + prefix: "test", + methods: { + get: () => {}, + set: () => {}, + remove: () => {}, + }, + key: "myTestStorage", + }; + const storage = agile.Storage(storageConfig); + + expect(storage).toBeInstanceOf(Storage); + expect(StorageMock).toHaveBeenCalledWith(storageConfig); + }); + }); + + describe("state function tests", () => { + it("should create State", () => { + const state = agile.State("testValue", { + key: "myCoolState", + }); + + expect(state).toBeInstanceOf(State); + }); + }); + + describe("collection function tests", () => { + const CollectionMock = Collection as jest.MockedClass; + + beforeEach(() => { + CollectionMock.mockClear(); + }); + + it("should create Collection", () => { + const collectionConfig = { + selectors: ["test", "test1"], + groups: ["test2", "test10"], + defaultGroupKey: "frank", + key: "myCoolCollection", + }; + + const collection = agile.Collection(collectionConfig); + + expect(collection).toBeInstanceOf(Collection); + expect(CollectionMock).toHaveBeenCalledWith(agile, collectionConfig); + }); + }); + + describe("computed function tests", () => { + const ComputedMock = Computed as jest.MockedClass; + + beforeEach(() => { + ComputedMock.mockClear(); + }); + + it("should create Computed", () => { + const computedFunction = () => { + // console.log("Hello Jeff"); + }; + + const computed = agile.Computed(computedFunction, []); + + expect(computed).toBeInstanceOf(Computed); + expect(ComputedMock).toHaveBeenCalledWith(agile, computedFunction, { + computedDeps: [], + }); + }); + }); + + describe("event function tests", () => { + const EventMock = Event as jest.MockedClass; + + beforeEach(() => { + EventMock.mockClear(); + }); + + it("should create Event", () => { + const eventConfig = { + rerender: true, + delay: 1000, + enabled: true, + key: "myCoolEvent", + }; + + const event = agile.Event(eventConfig); + + expect(event).toBeInstanceOf(Event); + expect(EventMock).toHaveBeenCalledWith(agile, eventConfig); + }); + }); + + describe("integrate function tests", () => { + it("should integrate provided Framework", () => { + const returnedAgile = agile.integrate(testIntegration); + + expect(returnedAgile).toBe(agile); + expect(agile.integrations.integrate).toHaveBeenCalledWith( + testIntegration + ); + }); + }); + + describe("registerStorage function tests", () => { + it("should register provided Storage", () => { + const dummyStorage = new Storage({ + prefix: "test", + methods: { + get: () => {}, + set: () => {}, + remove: () => {}, + }, + key: "myTestStorage", + }); + + const returnedAgile = agile.registerStorage(dummyStorage, { + default: false, + }); + + expect(returnedAgile).toBe(agile); + expect(agile.storages.register).toHaveBeenCalledWith(dummyStorage, { + default: false, + }); + }); + }); + + describe("hasIntegration function tests", () => { + it("should check if Agile has any registered Integration", () => { + agile.hasIntegration(); + + expect(agile.integrations.hasIntegration).toHaveBeenCalled(); + }); + }); + + describe("hasStorage function tests", () => { + it("should check if Agile has any registered Storage", () => { + agile.hasStorage(); + + expect(agile.storages.hasStorage).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/collection/collection.persistent.test.ts b/packages/core/tests/unit/collection/collection.persistent.test.ts new file mode 100644 index 00000000..9d5ece58 --- /dev/null +++ b/packages/core/tests/unit/collection/collection.persistent.test.ts @@ -0,0 +1,1120 @@ +import { + Agile, + CollectionPersistent, + Collection, + Storage, + Persistent, + StatePersistent, + Group, + Item, +} from "../../../src"; + +describe("CollectionPersistent Tests", () => { + interface ItemInterface { + id: string; + name: string; + } + + let dummyAgile: Agile; + let dummyCollection: Collection; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyAgile = new Agile({ localStorage: false }); + dummyCollection = new Collection(dummyAgile, { + key: "dummyCollectionKey", + }); + + jest.spyOn(CollectionPersistent.prototype, "instantiatePersistent"); + jest.spyOn(CollectionPersistent.prototype, "initialLoading"); + console.error = jest.fn(); + console.warn = jest.fn(); + }); + + it("should create CollectionPersistent and shouldn't call initialLoading if Persistent isn't ready (default config)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(CollectionPersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = false; + }); + + const collectionPersistent = new CollectionPersistent(dummyCollection); + + expect(collectionPersistent).toBeInstanceOf(CollectionPersistent); + expect(collectionPersistent.collection()).toBe(dummyCollection); + expect(collectionPersistent.instantiatePersistent).toHaveBeenCalledWith({ + key: undefined, + storageKeys: [], + }); + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + + expect(collectionPersistent._key).toBe(CollectionPersistent.placeHolderKey); + expect(collectionPersistent.ready).toBeFalsy(); + expect(collectionPersistent.isPersisted).toBeFalsy(); + expect(collectionPersistent.onLoad).toBeUndefined(); + expect(collectionPersistent.storageKeys).toStrictEqual([]); + expect(collectionPersistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create CollectionPersistent and shouldn't call initialLoading if Persistent isn't ready (specific config)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(CollectionPersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = false; + }); + + const collectionPersistent = new CollectionPersistent(dummyCollection, { + key: "collectionPersistentKey", + storageKeys: ["test1", "test2"], + }); + + expect(collectionPersistent).toBeInstanceOf(CollectionPersistent); + expect(collectionPersistent.instantiatePersistent).toHaveBeenCalledWith({ + key: "collectionPersistentKey", + storageKeys: ["test1", "test2"], + }); + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + + expect(collectionPersistent._key).toBe(CollectionPersistent.placeHolderKey); + expect(collectionPersistent.ready).toBeFalsy(); + expect(collectionPersistent.isPersisted).toBeFalsy(); + expect(collectionPersistent.onLoad).toBeUndefined(); + expect(collectionPersistent.storageKeys).toStrictEqual([]); + expect(collectionPersistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create CollectionPersistent and should call initialLoading if Persistent is ready (default config)", () => { + // Overwrite instantiatePersistent once to not call it + jest + .spyOn(CollectionPersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = true; + }); + + const collectionPersistent = new CollectionPersistent(dummyCollection); + + expect(collectionPersistent.initialLoading).toHaveBeenCalled(); + }); + + it("should create CollectionPersistent and shouldn't call initialLoading if Persistent is ready (config.instantiate = false)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(CollectionPersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = true; + }); + + const collectionPersistent = new CollectionPersistent(dummyCollection, { + instantiate: false, + }); + + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + }); + + describe("CollectionPersistent Function Tests", () => { + let collectionPersistent: CollectionPersistent; + let dummyItem1: Item; + let dummyItem2: Item; + let dummyItem3: Item; + let dummyItem4WithoutPersistent: Item; + + beforeEach(() => { + collectionPersistent = new CollectionPersistent(dummyCollection, { + key: "collectionPersistentKey", + storageKeys: ["dummyStorage"], + }); + dummyAgile.registerStorage( + new Storage({ + key: "dummyStorage", + methods: { + get: jest.fn(), + remove: jest.fn(), + set: jest.fn(), + }, + }) + ); + dummyItem1 = new Item(dummyCollection, { + id: "1", + name: "frank", + }); + dummyItem1.persistent = new StatePersistent(dummyItem1); + + dummyItem2 = new Item(dummyCollection, { + id: "2", + name: "dieter", + }); + dummyItem2.persistent = new StatePersistent(dummyItem2); + + dummyItem3 = new Item(dummyCollection, { + id: "3", + name: "hans", + }); + dummyItem3.persistent = new StatePersistent(dummyItem3); + + dummyItem4WithoutPersistent = new Item(dummyCollection, { + id: "4", + name: "jeff", + }); + }); + + describe("setKey function tests", () => { + beforeEach(() => { + collectionPersistent.removePersistedValue = jest.fn(); + collectionPersistent.persistValue = jest.fn(); + collectionPersistent.initialLoading = jest.fn(); + jest.spyOn(collectionPersistent, "validatePersistent"); + }); + + it("should update key with valid key in ready Persistent", async () => { + collectionPersistent.ready = true; + collectionPersistent._key = "dummyKey"; + + await collectionPersistent.setKey("newKey"); + + expect(collectionPersistent._key).toBe("newKey"); + expect(collectionPersistent.ready).toBeTruthy(); + expect(collectionPersistent.validatePersistent).toHaveBeenCalled(); + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + expect(collectionPersistent.persistValue).toHaveBeenCalledWith( + "newKey" + ); + expect(collectionPersistent.removePersistedValue).toHaveBeenCalledWith( + "dummyKey" + ); + }); + + it("should update key with not valid key in ready Persistent", async () => { + collectionPersistent.ready = true; + collectionPersistent._key = "dummyKey"; + + await collectionPersistent.setKey(); + + expect(collectionPersistent._key).toBe( + CollectionPersistent.placeHolderKey + ); + expect(collectionPersistent.ready).toBeFalsy(); + expect(collectionPersistent.validatePersistent).toHaveBeenCalled(); + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + expect(collectionPersistent.removePersistedValue).toHaveBeenCalledWith( + "dummyKey" + ); + }); + + it("should update key with valid key in not ready Persistent", async () => { + collectionPersistent.ready = false; + + await collectionPersistent.setKey("newKey"); + + expect(collectionPersistent._key).toBe("newKey"); + expect(collectionPersistent.ready).toBeTruthy(); + expect(collectionPersistent.validatePersistent).toHaveBeenCalled(); + expect(collectionPersistent.initialLoading).toHaveBeenCalled(); + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + expect( + collectionPersistent.removePersistedValue + ).not.toHaveBeenCalled(); + }); + + it("should update key with not valid key in not ready Persistent", async () => { + collectionPersistent.ready = false; + + await collectionPersistent.setKey(); + + expect(collectionPersistent._key).toBe( + CollectionPersistent.placeHolderKey + ); + expect(collectionPersistent.ready).toBeFalsy(); + expect(collectionPersistent.validatePersistent).toHaveBeenCalled(); + expect(collectionPersistent.initialLoading).not.toHaveBeenCalled(); + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + expect( + collectionPersistent.removePersistedValue + ).not.toHaveBeenCalled(); + }); + }); + + describe("initialLoading function tests", () => { + beforeEach(() => { + jest.spyOn(Persistent.prototype, "initialLoading"); + }); + + it("should initialLoad and set isPersisted in Collection to true", async () => { + await collectionPersistent.initialLoading(); + + expect(Persistent.prototype.initialLoading).toHaveBeenCalled(); + expect(dummyCollection.isPersisted).toBeTruthy(); + }); + }); + + describe("loadPersistedValue function tests", () => { + let dummyDefaultGroup: Group; + + beforeEach(() => { + collectionPersistent.defaultStorageKey = "test"; + + dummyDefaultGroup = new Group(dummyCollection, ["1", "2", "3"]); + dummyDefaultGroup.persistent = new StatePersistent(dummyDefaultGroup); + dummyDefaultGroup.persistent.ready = true; + + collectionPersistent.persistValue = jest.fn(); + + dummyDefaultGroup.persist = jest.fn(); + dummyDefaultGroup.persistent.initialLoading = jest.fn(); + + dummyCollection.collect = jest.fn(); + }); + + it("should load default Group and its Items with the persistentKey and apply it to the Collection if loading was successful", async () => { + collectionPersistent.ready = true; + dummyAgile.storages.get = jest + .fn() + .mockReturnValueOnce(Promise.resolve(true)) + .mockReturnValueOnce({ id: "1", name: "hans" }) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce({ id: "3", name: "frank" }); + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.loadPersistedValue(); + + expect(response).toBeTruthy(); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + collectionPersistent._key, + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "1", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "2", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "3", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + + expect(dummyDefaultGroup.persist).toHaveBeenCalledWith({ + instantiate: false, + followCollectionPersistKeyPattern: true, + }); + expect(dummyDefaultGroup.persistent.initialLoading).toHaveBeenCalled(); + expect(dummyDefaultGroup.isPersisted).toBeTruthy(); + + expect(dummyCollection.collect).toHaveBeenCalledWith({ + id: "1", + name: "hans", + }); + expect(dummyCollection.collect).not.toHaveBeenCalledWith(undefined); + expect(dummyCollection.collect).toHaveBeenCalledWith({ + id: "3", + name: "frank", + }); + + expect(collectionPersistent.persistValue).toHaveBeenCalledWith( + collectionPersistent._key + ); + }); + + it("shouldn't load default Group and its Items with the persistentKey and shouldn't apply it to the Collection if loading wasn't successful", async () => { + collectionPersistent.ready = true; + dummyAgile.storages.get = jest + .fn() + .mockReturnValueOnce(Promise.resolve(undefined)); + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.loadPersistedValue(); + + expect(response).toBeFalsy(); + + expect(dummyCollection.getGroup).not.toHaveBeenCalled(); + + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + collectionPersistent._key, + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "1", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "2", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "3", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + + expect(dummyDefaultGroup.persist).not.toHaveBeenCalled(); + expect( + dummyDefaultGroup.persistent.initialLoading + ).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.isPersisted).toBeFalsy(); + + expect(dummyCollection.collect).not.toHaveBeenCalled(); + + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + }); + + it("should load default Group and its Items with a specific Key and should apply it to the Collection if loading was successful", async () => { + collectionPersistent.ready = true; + dummyAgile.storages.get = jest + .fn() + .mockReturnValueOnce(Promise.resolve(true)) + .mockReturnValueOnce({ id: "1", name: "hans" }) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce({ id: "3", name: "frank" }); + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.loadPersistedValue( + "dummyKey" + ); + + expect(response).toBeTruthy(); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + "dummyKey", + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("1", "dummyKey"), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("2", "dummyKey"), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("3", "dummyKey"), + collectionPersistent.defaultStorageKey + ); + + expect(dummyDefaultGroup.persist).toHaveBeenCalledWith({ + instantiate: false, + followCollectionPersistKeyPattern: true, + }); + expect(dummyDefaultGroup.persistent.initialLoading).toHaveBeenCalled(); + expect(dummyDefaultGroup.isPersisted).toBeTruthy(); + + expect(dummyCollection.collect).toHaveBeenCalledWith({ + id: "1", + name: "hans", + }); + expect(dummyCollection.collect).not.toHaveBeenCalledWith(undefined); + expect(dummyCollection.collect).toHaveBeenCalledWith({ + id: "3", + name: "frank", + }); + + expect(collectionPersistent.persistValue).toHaveBeenCalledWith( + "dummyKey" + ); + }); + + it("shouldn't load default Group and its Items if Persistent isn't ready", async () => { + collectionPersistent.ready = false; + dummyAgile.storages.get = jest.fn(() => + Promise.resolve(undefined as any) + ); + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.loadPersistedValue(); + + expect(response).toBeFalsy(); + + expect(dummyCollection.getGroup).not.toHaveBeenCalled(); + + expect(dummyAgile.storages.get).not.toHaveBeenCalled(); + + expect(dummyDefaultGroup.persist).not.toHaveBeenCalled(); + expect( + dummyDefaultGroup.persistent.initialLoading + ).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.isPersisted).toBeFalsy(); + + expect(dummyCollection.collect).not.toHaveBeenCalled(); + + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + }); + + it("shouldn't load default Group and its Items if Collection has no defaultGroup", async () => { + collectionPersistent.ready = true; + dummyCollection.groups = {}; + dummyAgile.storages.get = jest + .fn() + .mockReturnValueOnce(Promise.resolve(true)); + dummyCollection.getGroup = jest.fn(() => undefined); + + const response = await collectionPersistent.loadPersistedValue(); + + expect(response).toBeFalsy(); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + collectionPersistent._key, + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "1", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "2", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + expect(dummyAgile.storages.get).not.toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "3", + collectionPersistent._key + ), + collectionPersistent.defaultStorageKey + ); + + expect(dummyDefaultGroup.persist).not.toHaveBeenCalled(); + expect( + dummyDefaultGroup.persistent.initialLoading + ).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.isPersisted).toBeFalsy(); + + expect(dummyCollection.collect).not.toHaveBeenCalled(); + + expect(collectionPersistent.persistValue).not.toHaveBeenCalled(); + }); + }); + + describe("persistValue function tests", () => { + let dummyDefaultGroup: Group; + + beforeEach(() => { + collectionPersistent.storageKeys = ["test1", "test2"]; + collectionPersistent.isPersisted = undefined; + + dummyDefaultGroup = new Group(dummyCollection, ["1", "2", "3"]); + dummyCollection.data = { + ["1"]: dummyItem1, + ["3"]: dummyItem3, + }; + + dummyDefaultGroup.persist = jest.fn(); + jest.spyOn(dummyDefaultGroup, "addSideEffect"); + + dummyItem1.persist = jest.fn(); + dummyItem3.persist = jest.fn(); + + dummyCollection.collect = jest.fn(); + + dummyAgile.storages.set = jest.fn(); + }); + + it("should persist defaultGroup and its Items with persistentKey", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.persistValue(); + + expect(response).toBeTruthy(); + + expect(dummyAgile.storages.set).toHaveBeenCalledWith( + collectionPersistent._key, + true, + collectionPersistent.storageKeys + ); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect(dummyDefaultGroup.persist).toHaveBeenCalledWith({ + followCollectionPersistKeyPattern: true, + }); + expect(dummyDefaultGroup.addSideEffect).toHaveBeenCalledWith( + CollectionPersistent.defaultGroupSideEffectKey, + expect.any(Function) + ); + + expect(dummyItem1.persist).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + dummyItem1._key, + collectionPersistent._key + ) + ); + expect(dummyItem3.persist).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + dummyItem3._key, + collectionPersistent._key + ) + ); + + expect(collectionPersistent.isPersisted).toBeTruthy(); + }); + + it("should persist defaultGroup and its Items with specific Key", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.persistValue("dummyKey"); + + expect(response).toBeTruthy(); + + expect(dummyAgile.storages.set).toHaveBeenCalledWith( + "dummyKey", + true, + collectionPersistent.storageKeys + ); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect(dummyDefaultGroup.persist).toHaveBeenCalledWith({ + followCollectionPersistKeyPattern: true, + }); + expect(dummyDefaultGroup.addSideEffect).toHaveBeenCalledWith( + CollectionPersistent.defaultGroupSideEffectKey, + expect.any(Function) + ); + + expect(dummyItem1.persist).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey(dummyItem1._key, "dummyKey") + ); + expect(dummyItem3.persist).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey(dummyItem3._key, "dummyKey") + ); + + expect(collectionPersistent.isPersisted).toBeTruthy(); + }); + + it("shouldn't persist defaultGroup and its Items if Persistent isn't ready", async () => { + collectionPersistent.ready = false; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.persistValue("dummyKey"); + + expect(response).toBeFalsy(); + + expect(dummyAgile.storages.set).not.toHaveBeenCalled(); + + expect(dummyCollection.getGroup).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.persist).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.addSideEffect).not.toHaveBeenCalled(); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeUndefined(); + }); + + it("shouldn't persist defaultGroup and its Items if Collection has no defaultGroup", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => undefined as any); + + const response = await collectionPersistent.persistValue(); + + expect(response).toBeFalsy(); + + expect(dummyAgile.storages.set).not.toHaveBeenCalled(); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect(dummyDefaultGroup.persist).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.addSideEffect).not.toHaveBeenCalled(); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeUndefined(); + }); + + describe("test added sideEffect called CollectionPersistent.defaultGroupSideEffectKey", () => { + beforeEach(() => { + collectionPersistent.rebuildStorageSideEffect = jest.fn(); + }); + + it("should call rebuildStorageSideEffect with persistentKey", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + await collectionPersistent.persistValue(); + + dummyDefaultGroup.sideEffects[ + CollectionPersistent.defaultGroupSideEffectKey + ](); + + expect( + collectionPersistent.rebuildStorageSideEffect + ).toHaveBeenCalledWith(dummyDefaultGroup, collectionPersistent._key); + }); + + it("should call rebuildStorageSideEffect with specific Key", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + await collectionPersistent.persistValue("dummyKey"); + + dummyDefaultGroup.sideEffects[ + CollectionPersistent.defaultGroupSideEffectKey + ](); + + expect( + collectionPersistent.rebuildStorageSideEffect + ).toHaveBeenCalledWith(dummyDefaultGroup, "dummyKey"); + }); + }); + }); + + describe("removePersistedValue function tests", () => { + let dummyDefaultGroup: Group; + + beforeEach(() => { + collectionPersistent.storageKeys = ["test1", "test2"]; + collectionPersistent.isPersisted = undefined; + + dummyDefaultGroup = new Group(dummyCollection, ["1", "2", "3"]); + dummyDefaultGroup.persistent = new StatePersistent(dummyDefaultGroup); + dummyCollection.data = { + ["1"]: dummyItem1, + ["3"]: dummyItem3, + }; + + dummyDefaultGroup.persistent.removePersistedValue = jest.fn(); + dummyDefaultGroup.removeSideEffect = jest.fn(); + + dummyItem1.persistent.removePersistedValue = jest.fn(); + dummyItem3.persistent.removePersistedValue = jest.fn(); + + dummyAgile.storages.remove = jest.fn(); + }); + + it("should remove persisted defaultGroup and its Items from Storage with persistentKey", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.removePersistedValue(); + + expect(response).toBeTruthy(); + + expect(dummyAgile.storages.remove).toHaveBeenCalledWith( + collectionPersistent._key, + collectionPersistent.storageKeys + ); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect( + dummyDefaultGroup.persistent.removePersistedValue + ).toHaveBeenCalled(); + expect(dummyDefaultGroup.removeSideEffect).toHaveBeenCalledWith( + CollectionPersistent.defaultGroupSideEffectKey + ); + + expect(dummyItem1.persistent.removePersistedValue).toHaveBeenCalled(); + expect(dummyItem3.persistent.removePersistedValue).toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeFalsy(); + }); + + it("should remove persisted defaultGroup and its Items from Storage with specific Key", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.removePersistedValue( + "dummyKey" + ); + + expect(response).toBeTruthy(); + + expect(dummyAgile.storages.remove).toHaveBeenCalledWith( + "dummyKey", + collectionPersistent.storageKeys + ); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect( + dummyDefaultGroup.persistent.removePersistedValue + ).toHaveBeenCalled(); + expect(dummyDefaultGroup.removeSideEffect).toHaveBeenCalledWith( + CollectionPersistent.defaultGroupSideEffectKey + ); + + expect(dummyItem1.persistent.removePersistedValue).toHaveBeenCalled(); + expect(dummyItem3.persistent.removePersistedValue).toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeFalsy(); + }); + + it("shouldn't remove persisted defaultGroup and its Items from Storage if Persistent isn't ready", async () => { + collectionPersistent.ready = false; + dummyCollection.getGroup = jest.fn(() => dummyDefaultGroup as any); + + const response = await collectionPersistent.removePersistedValue(); + + expect(response).toBeFalsy(); + + expect(dummyAgile.storages.remove).not.toHaveBeenCalled(); + + expect(dummyCollection.getGroup).not.toHaveBeenCalled(); + expect( + dummyDefaultGroup.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.removeSideEffect).not.toHaveBeenCalled(); + + expect( + dummyItem1.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem3.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeUndefined(); + }); + + it("shouldn't remove persisted defaultGroup and its Items from Storage if Collection has no default Group", async () => { + collectionPersistent.ready = true; + dummyCollection.getGroup = jest.fn(() => undefined as any); + + const response = await collectionPersistent.removePersistedValue(); + + expect(response).toBeFalsy(); + + expect(dummyAgile.storages.remove).not.toHaveBeenCalled(); + + expect(dummyCollection.getGroup).toHaveBeenCalledWith( + dummyCollection.config.defaultGroupKey + ); + expect( + dummyDefaultGroup.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect(dummyDefaultGroup.removeSideEffect).not.toHaveBeenCalled(); + + expect( + dummyItem1.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem3.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(collectionPersistent.isPersisted).toBeUndefined(); + }); + }); + + describe("formatKey function tests", () => { + it("should return key of Collection if no key got passed", () => { + dummyCollection._key = "coolKey"; + + const response = collectionPersistent.formatKey(); + + expect(response).toBe("coolKey"); + }); + + it("should return passed key", () => { + dummyCollection._key = "coolKey"; + + const response = collectionPersistent.formatKey("awesomeKey"); + + expect(response).toBe("awesomeKey"); + }); + + it("should return and apply passed key to Collection if Collection had no own key before", () => { + dummyCollection._key = undefined; + + const response = collectionPersistent.formatKey("awesomeKey"); + + expect(response).toBe("awesomeKey"); + expect(dummyCollection._key).toBe("awesomeKey"); + }); + + it("should return undefined if no key got passed and Collection has no key", () => { + dummyCollection._key = undefined; + + const response = collectionPersistent.formatKey(); + + expect(response).toBeUndefined(); + }); + }); + + describe("rebuildStorageSideEffects function tests", () => { + let dummyGroup: Group; + + beforeEach(() => { + dummyGroup = new Group(dummyCollection); + dummyCollection.data = { + ["1"]: dummyItem1, + ["2"]: dummyItem2, + ["3"]: dummyItem3, + ["4"]: dummyItem4WithoutPersistent, + }; + dummyCollection.persistent = collectionPersistent; + + dummyItem1.persist = jest.fn(); + dummyItem2.persist = jest.fn(); + dummyItem3.persist = jest.fn(); + dummyItem4WithoutPersistent.persist = jest.fn(); + + dummyItem1.persistent.removePersistedValue = jest.fn(); + dummyItem2.persistent.removePersistedValue = jest.fn(); + dummyItem3.persistent.removePersistedValue = jest.fn(); + + dummyItem1.persistent.persistValue = jest.fn(); + dummyItem2.persistent.persistValue = jest.fn(); + dummyItem3.persistent.persistValue = jest.fn(); + }); + + it("should return if no Item got added or removed", () => { + dummyGroup.previousStateValue = ["1", "2", "3"]; + dummyGroup._value = ["1", "2", "3"]; + + collectionPersistent.rebuildStorageSideEffect(dummyGroup); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem2.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + expect(dummyItem4WithoutPersistent.persist).not.toHaveBeenCalled(); + + expect( + dummyItem1.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem2.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem3.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(dummyItem1.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem2.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem3.persistent.persistValue).not.toHaveBeenCalled(); + }); + + it("should call removePersistedValue on Items that got removed from Group", () => { + dummyGroup.previousStateValue = ["1", "2", "3"]; + dummyGroup._value = ["2"]; + + collectionPersistent.rebuildStorageSideEffect(dummyGroup); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem2.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + expect(dummyItem4WithoutPersistent.persist).not.toHaveBeenCalled(); + + expect(dummyItem1.persistent.removePersistedValue).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("1", collectionPersistent._key) + ); + expect( + dummyItem2.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect(dummyItem3.persistent.removePersistedValue).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("3", collectionPersistent._key) + ); + + expect(dummyItem1.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem2.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem3.persistent.persistValue).not.toHaveBeenCalled(); + }); + + it("should call persistValue on Items that have a persistent and got added to Group", () => { + dummyGroup.previousStateValue = ["1"]; + dummyGroup._value = ["1", "2", "3"]; + + collectionPersistent.rebuildStorageSideEffect(dummyGroup); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem2.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + expect(dummyItem4WithoutPersistent.persist).not.toHaveBeenCalled(); + + expect( + dummyItem1.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem2.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem3.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(dummyItem1.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem2.persistent.persistValue).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("2", collectionPersistent._key) + ); + expect(dummyItem3.persistent.persistValue).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("3", collectionPersistent._key) + ); + }); + + it("should call persist on Items that have no persistent and got added to Group", () => { + dummyGroup.previousStateValue = ["1"]; + dummyGroup._value = ["1", "4"]; + + collectionPersistent.rebuildStorageSideEffect(dummyGroup); + + expect(dummyItem1.persist).not.toHaveBeenCalled(); + expect(dummyItem2.persist).not.toHaveBeenCalled(); + expect(dummyItem3.persist).not.toHaveBeenCalled(); + expect(dummyItem4WithoutPersistent.persist).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey("4", collectionPersistent._key) + ); + + expect( + dummyItem1.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem2.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + expect( + dummyItem3.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(dummyItem1.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem2.persistent.persistValue).not.toHaveBeenCalled(); + expect(dummyItem3.persistent.persistValue).not.toHaveBeenCalled(); + }); + }); + + describe("getItemStorageKey function tests", () => { + beforeEach(() => { + console.warn = jest.fn(); + }); + + it("should build ItemStorageKey out of itemKey and collectionKey", () => { + const response = CollectionPersistent.getItemStorageKey( + "itemKey", + "collectionKey" + ); + + expect(response).toBe("_collectionKey_item_itemKey"); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it("should build ItemStorageKey out of collectionKey with warning", () => { + const response = CollectionPersistent.getItemStorageKey( + undefined, + "collectionKey" + ); + + expect(response).toBe("_collectionKey_item_unknown"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Item StorageKey!" + ); + }); + + it("should build ItemStorageKey out of itemKey with warning", () => { + const response = CollectionPersistent.getItemStorageKey( + "itemKey", + undefined + ); + + expect(response).toBe("_unknown_item_itemKey"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Item StorageKey!" + ); + }); + + it("should build ItemStorageKey out of nothing with warning", () => { + const response = CollectionPersistent.getItemStorageKey( + undefined, + undefined + ); + + expect(response).toBe("_unknown_item_unknown"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Item StorageKey!" + ); + }); + }); + + describe("getGroupStorageKey function tests", () => { + beforeEach(() => { + console.warn = jest.fn(); + }); + + it("should build GroupStorageKey out of groupKey and collectionKey", () => { + const response = CollectionPersistent.getGroupStorageKey( + "groupKey", + "collectionKey" + ); + + expect(response).toBe("_collectionKey_group_groupKey"); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it("should build GroupStorageKey out of collectionKey with warning", () => { + const response = CollectionPersistent.getGroupStorageKey( + undefined, + "collectionKey" + ); + + expect(response).toBe("_collectionKey_group_unknown"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Group StorageKey!" + ); + }); + + it("should build GroupStorageKey out of groupKey with warning", () => { + const response = CollectionPersistent.getGroupStorageKey( + "groupKey", + undefined + ); + + expect(response).toBe("_unknown_group_groupKey"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Group StorageKey!" + ); + }); + + it("should build GroupStorageKey out of nothing with warning", () => { + const response = CollectionPersistent.getGroupStorageKey( + undefined, + undefined + ); + + expect(response).toBe("_unknown_group_unknown"); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Failed to build unique Group StorageKey!" + ); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/collection/collection.test.ts b/packages/core/tests/unit/collection/collection.test.ts new file mode 100644 index 00000000..41ce33a2 --- /dev/null +++ b/packages/core/tests/unit/collection/collection.test.ts @@ -0,0 +1,2061 @@ +import { + Collection, + Agile, + Group, + Selector, + Item, + CollectionPersistent, + ComputedTracker, + StatePersistent, +} from "../../../src"; +import * as Utils from "../../../src/utils"; + +jest.mock("../../../src/collection/collection.persistent"); + +describe("Collection Tests", () => { + interface ItemInterface { + id: string; + name: string; + } + let dummyAgile: Agile; + + beforeEach(() => { + jest.clearAllMocks(); + dummyAgile = new Agile({ localStorage: false }); + + jest.spyOn(Collection.prototype, "initSelectors"); + jest.spyOn(Collection.prototype, "initGroups"); + console.error = jest.fn(); + console.warn = jest.fn(); + }); + + it("should create Collection (default config)", () => { + // Overwrite methods once to not call id + jest + .spyOn(Collection.prototype, "initSelectors") + .mockReturnValueOnce(undefined); + jest + .spyOn(Collection.prototype, "initGroups") + .mockReturnValueOnce(undefined); + + const collection = new Collection(dummyAgile); + + expect(collection.config).toStrictEqual({ + primaryKey: "id", + defaultGroupKey: "default", + }); + expect(collection.size).toBe(0); + expect(collection.data).toStrictEqual({}); + expect(collection._key).toBeUndefined(); + expect(collection.isPersisted).toBeFalsy(); + expect(collection.persistent).toBeUndefined(); + expect(collection.groups).toStrictEqual({}); + expect(collection.selectors).toStrictEqual({}); + + expect(Collection.prototype.initGroups).toHaveBeenCalledWith({}); + expect(Collection.prototype.initSelectors).toHaveBeenCalledWith({}); + }); + + it("should create Collection (specific config)", () => { + // Overwrite methods once to not call id + jest + .spyOn(Collection.prototype, "initSelectors") + .mockReturnValueOnce(undefined); + jest + .spyOn(Collection.prototype, "initGroups") + .mockReturnValueOnce(undefined); + + const collection = new Collection(dummyAgile, { + defaultGroupKey: "general", + groups: ["group1", "group2"], + selectors: ["selector1", "selector2"], + key: "dummyCollectionKey", + primaryKey: "key", + }); + + expect(collection.config).toStrictEqual({ + primaryKey: "key", + defaultGroupKey: "general", + }); + expect(collection.size).toBe(0); + expect(collection.data).toStrictEqual({}); + expect(collection._key).toBe("dummyCollectionKey"); + expect(collection.isPersisted).toBeFalsy(); + expect(collection.persistent).toBeUndefined(); + expect(collection.groups).toStrictEqual({}); + expect(collection.selectors).toStrictEqual({}); + + expect(Collection.prototype.initGroups).toHaveBeenCalledWith([ + "group1", + "group2", + ]); + expect(Collection.prototype.initSelectors).toHaveBeenCalledWith([ + "selector1", + "selector2", + ]); + }); + + it("should create Collection (specific config in function form)", () => { + // Overwrite methods once to not call id + jest + .spyOn(Collection.prototype, "initSelectors") + .mockReturnValueOnce(undefined); + jest + .spyOn(Collection.prototype, "initGroups") + .mockReturnValueOnce(undefined); + + const collection = new Collection( + dummyAgile, + (collection) => ({ + defaultGroupKey: "general", + groups: { + group1: collection.Group(), + }, + selectors: { + selector1: collection.Selector("id1"), + }, + key: "dummyCollectionKey", + primaryKey: "key", + }) + ); + + expect(collection.config).toStrictEqual({ + primaryKey: "key", + defaultGroupKey: "general", + }); + expect(collection.size).toBe(0); + expect(collection.data).toStrictEqual({ + id1: expect.any(Item), // Placeholder Item created by Selector + }); + expect(collection._key).toBe("dummyCollectionKey"); + expect(collection.isPersisted).toBeFalsy(); + expect(collection.persistent).toBeUndefined(); + expect(collection.groups).toStrictEqual({}); + expect(collection.selectors).toStrictEqual({}); + + expect(Collection.prototype.initGroups).toHaveBeenCalledWith({ + group1: expect.any(Group), + }); + expect(Collection.prototype.initSelectors).toHaveBeenCalledWith({ + selector1: expect.any(Selector), + }); + }); + + describe("Collection Function Tests", () => { + let collection: Collection; + + beforeEach(() => { + collection = new Collection(dummyAgile, { key: "collectionKey" }); + }); + + it("should call setKey with passed value", () => { + collection.setKey = jest.fn(); + + collection.key = "newKey"; + + expect(collection.setKey).toHaveBeenCalledWith("newKey"); + }); + + describe("key get function tests", () => { + it("should return current State Key", () => { + expect(collection.key).toBe("collectionKey"); + }); + }); + + describe("setKey function tests", () => { + beforeEach(() => { + collection.persistent = new CollectionPersistent(collection); + + collection.persistent.setKey = jest.fn(); + }); + + it("should update existing Key in all instances", () => { + collection.persistent._key = "collectionKey"; + + collection.setKey("newKey"); + + expect(collection._key).toBe("newKey"); + expect(collection.persistent.setKey).toHaveBeenCalledWith("newKey"); + }); + + it("should update existing Key in all instances except persistent if the CollectionKey and PersistKey aren't equal", () => { + collection.persistent._key = "randomKey"; + + collection.setKey("newKey"); + + expect(collection._key).toBe("newKey"); + expect(collection.persistent.setKey).not.toHaveBeenCalled(); + }); + + it("should update existing Key in all instances except persistent if new CollectionKey is undefined", () => { + collection.persistent._key = "collectionKey"; + + collection.setKey(undefined); + + expect(collection._key).toBeUndefined(); + expect(collection.persistent.setKey).not.toHaveBeenCalled(); + }); + }); + + describe("Group function tests", () => { + it("should create Group which belongs to Collection", () => { + const response = collection.Group([1, 2], { + key: "group1Key", + }); + + expect(response).toBeInstanceOf(Group); + expect(response._key).toBe("group1Key"); + expect(response._value).toStrictEqual([1, 2]); + expect(response.collection()).toBe(collection); + }); + }); + + describe("Selector function tests", () => { + it("should create Selector which belongs to Collection", () => { + const response = collection.Selector("id1", { + key: "selector1Key", + }); + + expect(response).toBeInstanceOf(Selector); + expect(response._key).toBe("selector1Key"); + expect(response._itemKey).toBe("id1"); + expect(response.collection()).toBe(collection); + }); + }); + + describe("initGroups function tests", () => { + it("should create GroupsObject out of passed GroupKeys Array and add defaultGroup", () => { + collection.initGroups(["group1", "group2"]); + + expect(collection.groups).toHaveProperty("group1"); + expect(collection.groups["group1"]._key).toBe("group1"); + expect(collection.groups["group1"]._value).toStrictEqual([]); + expect(collection.groups["group1"].collection()).toBe(collection); + + expect(collection.groups).toHaveProperty("group2"); + expect(collection.groups["group2"]._key).toBe("group2"); + expect(collection.groups["group2"]._value).toStrictEqual([]); + expect(collection.groups["group2"].collection()).toBe(collection); + + expect(collection.groups).toHaveProperty( + collection.config.defaultGroupKey as any + ); + expect(collection.groups[collection.config.defaultGroupKey]._key).toBe( + "default" + ); + expect( + collection.groups[collection.config.defaultGroupKey]._value + ).toStrictEqual([]); + expect( + collection.groups[collection.config.defaultGroupKey].collection() + ).toBe(collection); + }); + + it("should create GroupsObject out of passed Groups Object and add default Group", () => { + let dummyGroup1 = new Group(collection); + let dummyGroup2 = new Group(collection, ["test1", "test2"], { + key: "overwrittenKey", + }); + + collection.initGroups({ + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }); + + expect(collection.groups).toHaveProperty("dummyGroup1"); + expect(collection.groups["dummyGroup1"]._key).toBe("dummyGroup1"); + expect(collection.groups["dummyGroup1"]._value).toStrictEqual([]); + expect(collection.groups["dummyGroup1"].collection()).toBe(collection); + + expect(collection.groups).toHaveProperty("dummyGroup2"); + expect(collection.groups["dummyGroup2"]._key).toBe("overwrittenKey"); + expect(collection.groups["dummyGroup2"]._value).toStrictEqual([ + "test1", + "test2", + ]); + expect(collection.groups["dummyGroup2"].collection()).toBe(collection); + + expect(collection.groups).toHaveProperty( + collection.config.defaultGroupKey as any + ); + expect(collection.groups[collection.config.defaultGroupKey]._key).toBe( + "default" + ); + expect( + collection.groups[collection.config.defaultGroupKey]._value + ).toStrictEqual([]); + expect( + collection.groups[collection.config.defaultGroupKey].collection() + ).toBe(collection); + }); + }); + + describe("initSelectors function tests", () => { + it("should create SelectorsObject out of passed SelectorKeys Array", () => { + collection.initSelectors(["selector1", "selector2"]); + + expect(collection.selectors).toHaveProperty("selector1"); + expect(collection.selectors["selector1"]._key).toBe("selector1"); + expect(collection.selectors["selector1"]._itemKey).toBe("selector1"); + expect(collection.selectors["selector1"].collection()).toBe(collection); + + expect(collection.selectors).toHaveProperty("selector2"); + expect(collection.selectors["selector2"]._key).toBe("selector2"); + expect(collection.selectors["selector2"]._itemKey).toBe("selector2"); + expect(collection.selectors["selector2"].collection()).toBe(collection); + }); + + it("should create SelectorsObject out of passed Selector Object", () => { + let dummySelector1 = new Selector(collection, "1"); + let dummySelector2 = new Selector(collection, "2", { + key: "overwrittenKey", + }); + + collection.initSelectors({ + dummySelector1: dummySelector1, + dummySelector2: dummySelector2, + }); + + expect(collection.selectors).toHaveProperty("dummySelector1"); + expect(collection.selectors["dummySelector1"]._key).toBe( + "dummySelector1" + ); + expect(collection.selectors["dummySelector1"]._itemKey).toBe("1"); + expect(collection.selectors["dummySelector1"].collection()).toBe( + collection + ); + + expect(collection.selectors).toHaveProperty("dummySelector2"); + expect(collection.selectors["dummySelector2"]._key).toBe( + "overwrittenKey" + ); + expect(collection.selectors["dummySelector2"]._itemKey).toBe("2"); + expect(collection.selectors["dummySelector2"].collection()).toBe( + collection + ); + }); + }); + + describe("collect function tests", () => { + let dummyGroup1: Group; + let dummyGroup2: Group; + let defaultGroup: Group; + + beforeEach(() => { + dummyGroup1 = new Group(collection); + dummyGroup2 = new Group(collection); + defaultGroup = new Group(collection); + + collection.groups = { + [collection.config.defaultGroupKey]: defaultGroup, + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }; + + collection.setData = jest.fn(); + collection.createSelector = jest.fn(); + collection.createGroup = jest.fn(); + + dummyGroup1.add = jest.fn(); + dummyGroup2.add = jest.fn(); + defaultGroup.add = jest.fn(); + }); + + it("should add Data to Collection and to default Group (default config)", () => { + collection.setData = jest.fn(() => true); + + collection.collect({ id: "1", name: "frank" }); + + expect(collection.setData).toHaveBeenCalledWith( + { + id: "1", + name: "frank", + }, + { + patch: false, + background: false, + } + ); + expect(collection.createGroup).not.toHaveBeenCalled(); + + expect(dummyGroup1.add).not.toHaveBeenCalled(); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + expect(defaultGroup.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + + expect(collection.createSelector).not.toHaveBeenCalled(); + }); + + it("should add Data to Collection and to default Group (specific config)", () => { + collection.setData = jest.fn(() => true); + + collection.collect({ id: "1", name: "frank" }, [], { + background: true, + method: "unshift", + patch: true, + }); + + expect(collection.setData).toHaveBeenCalledWith( + { + id: "1", + name: "frank", + }, + { + patch: true, + background: true, + } + ); + expect(collection.createGroup).not.toHaveBeenCalled(); + + expect(dummyGroup1.add).not.toHaveBeenCalled(); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + expect(defaultGroup.add).toHaveBeenCalledWith("1", { + method: "unshift", + background: true, + }); + + expect(collection.createSelector).not.toHaveBeenCalled(); + }); + + it("should add Data to Collection and to passed Groups + default Group (default config)", () => { + collection.setData = jest.fn(() => true); + + collection.collect( + [ + { id: "1", name: "frank" }, + { id: "2", name: "hans" }, + ], + ["dummyGroup1", "dummyGroup2"] + ); + + expect(collection.setData).toHaveBeenCalledWith( + { + id: "1", + name: "frank", + }, + { + patch: false, + background: false, + } + ); + expect(collection.setData).toHaveBeenCalledWith( + { + id: "2", + name: "hans", + }, + { + patch: false, + background: false, + } + ); + expect(collection.createGroup).not.toHaveBeenCalled(); + + expect(dummyGroup1.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + expect(dummyGroup1.add).toHaveBeenCalledWith("2", { + method: "push", + background: false, + }); + expect(dummyGroup2.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + expect(dummyGroup2.add).toHaveBeenCalledWith("2", { + method: "push", + background: false, + }); + expect(defaultGroup.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + expect(defaultGroup.add).toHaveBeenCalledWith("2", { + method: "push", + background: false, + }); + + expect(collection.createSelector).not.toHaveBeenCalled(); + }); + + it("should call setData and shouldn't add Items to passed Groups if setData failed (default config)", () => { + collection.setData = jest.fn(() => false); + + collection.collect({ id: "1", name: "frank" }, [ + "dummyGroup1", + "dummyGroup2", + ]); + + expect(collection.setData).toHaveBeenCalledWith( + { + id: "1", + name: "frank", + }, + { + patch: false, + background: false, + } + ); + expect(collection.createGroup).not.toHaveBeenCalled(); + + expect(dummyGroup1.add).not.toHaveBeenCalled(); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + expect(defaultGroup.add).not.toHaveBeenCalled(); + + expect(collection.createSelector).not.toHaveBeenCalled(); + }); + + it("should add Data to Collection and create Groups that doesn't exist yet (default config)", () => { + const notExistingGroup = new Group(collection); + notExistingGroup.add = jest.fn(); + collection.setData = jest.fn(() => true); + collection.createGroup = jest.fn(function (groupKey) { + this.groups[groupKey] = notExistingGroup; + return notExistingGroup as any; + }); + + collection.collect({ id: "1", name: "frank" }, "notExistingGroup"); + + expect(collection.setData).toHaveBeenCalledWith( + { + id: "1", + name: "frank", + }, + { + patch: false, + background: false, + } + ); + expect(collection.createGroup).toHaveBeenCalledWith("notExistingGroup"); + + expect(dummyGroup1.add).not.toHaveBeenCalled(); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + expect(notExistingGroup.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + expect(defaultGroup.add).toHaveBeenCalledWith("1", { + method: "push", + background: false, + }); + + expect(collection.createSelector).not.toHaveBeenCalled(); + }); + + it("should create Selector for each Item (config.select)", () => { + collection.setData = jest.fn(() => true); + + collection.collect( + [ + { id: "1", name: "frank" }, + { id: "2", name: "hans" }, + ], + [], + { select: true } + ); + + expect(collection.createSelector).toHaveBeenCalledWith("1", "1"); + expect(collection.createSelector).toHaveBeenCalledWith("2", "2"); + }); + + it("should call 'forEachItem' for each Item (default config)", () => { + collection.setData = jest.fn(() => true); + const forEachItemMock = jest.fn(); + + collection.collect( + [ + { id: "1", name: "frank" }, + { id: "2", name: "hans" }, + ], + [], + { forEachItem: forEachItemMock } + ); + + expect(forEachItemMock).toHaveBeenCalledWith( + { id: "1", name: "frank" }, + "1", + 0 + ); + expect(forEachItemMock).toHaveBeenCalledWith( + { id: "2", name: "hans" }, + "2", + 1 + ); + }); + }); + + describe("update function tests", () => { + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "dummyItem", name: "frank" }); + collection.data = { + dummyItem: dummyItem, + }; + + dummyItem.set = jest.fn(); + collection.updateItemKey = jest.fn(); + jest.spyOn(Utils, "flatMerge"); + }); + + it("should update existing Item with valid changes Object (default config)", () => { + const response = collection.update("dummyItem", { name: "hans" }); + + expect(response).toBe(dummyItem); + expect(console.error).not.toHaveBeenCalled(); + expect(dummyItem.set).toHaveBeenCalledWith( + { + id: "dummyItem", + name: "hans", + }, + { + background: false, + storage: true, + } + ); + expect(Utils.flatMerge).toHaveBeenCalledWith( + { id: "dummyItem", name: "frank" }, + { name: "hans" }, + { + addNewProperties: false, + } + ); + expect(collection.updateItemKey).not.toHaveBeenCalled(); + }); + + it("should update existing Item with valid changes Object (specific config)", () => { + const response = collection.update( + "dummyItem", + { name: "hans" }, + { + addNewProperties: true, + background: true, + } + ); + + expect(response).toBe(dummyItem); + expect(console.error).not.toHaveBeenCalled(); + expect(dummyItem.set).toHaveBeenCalledWith( + { + id: "dummyItem", + name: "hans", + }, + { + background: true, + storage: true, + } + ); + expect(Utils.flatMerge).toHaveBeenCalledWith( + { id: "dummyItem", name: "frank" }, + { name: "hans" }, + { + addNewProperties: true, + } + ); + expect(collection.updateItemKey).not.toHaveBeenCalled(); + }); + + it("should update existing placeholder Item with valid changes Object (default config)", () => { + dummyItem.isPlaceholder = true; + + const response = collection.update("dummyItem", { name: "hans" }); + + expect(response).toBe(dummyItem); + expect(console.error).not.toHaveBeenCalled(); + expect(dummyItem.set).toHaveBeenCalledWith( + { + id: "dummyItem", + name: "hans", + }, + { + background: false, + storage: true, + } + ); + expect(Utils.flatMerge).toHaveBeenCalledWith( + { id: "dummyItem", name: "frank" }, + { name: "hans" }, + { + addNewProperties: false, + } + ); + expect(collection.updateItemKey).not.toHaveBeenCalled(); + }); + + it("shouldn't update not existing Item and should print error", () => { + const response = collection.update("notExisting", { name: "hans" }); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + `Agile Error: ItemKey 'notExisting' doesn't exist in Collection '${collection._key}'!` + ); + expect(dummyItem.set).not.toHaveBeenCalled(); + expect(Utils.flatMerge).not.toHaveBeenCalled(); + expect(collection.updateItemKey).not.toHaveBeenCalled(); + }); + + it("shouldn't update existing Item with invalid changes Object and should print error", () => { + const response = collection.update( + "dummyItem", + "notValidChanges" as any + ); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + `Agile Error: You have to pass an valid Changes Object to update 'dummyItem' in '${collection._key}'!` + ); + expect(dummyItem.set).not.toHaveBeenCalled(); + expect(Utils.flatMerge).not.toHaveBeenCalled(); + expect(collection.updateItemKey).not.toHaveBeenCalled(); + }); + + it("should update existing Item and its ItemKey with valid changes Object if ItemKey has changed (default config)", () => { + const response = collection.update("dummyItem", { + id: "newDummyItemKey", + name: "hans", + }); + + expect(response).toBe(dummyItem); + expect(console.error).not.toHaveBeenCalled(); + expect(dummyItem.set).toHaveBeenCalledWith( + { + id: "newDummyItemKey", + name: "hans", + }, + { + background: false, + storage: false, + } + ); + expect(Utils.flatMerge).toHaveBeenCalledWith( + { id: "dummyItem", name: "frank" }, + { id: "newDummyItemKey", name: "hans" }, + { + addNewProperties: false, + } + ); + expect(collection.updateItemKey).toHaveBeenCalledWith( + "dummyItem", + "newDummyItemKey", + { + background: false, + } + ); + }); + }); + + describe("createGroup function tests", () => { + let dummyGroup: Group; + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "dummyItem", name: "frank" }); + collection.data = { + dummyItem: dummyItem, + }; + + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + collection.groups = { + dummyGroup: dummyGroup, + }; + + dummyGroup.set = jest.fn(); + }); + + it("should create and add not existing Group to Collection", () => { + const response = collection.createGroup("newGroup", ["dummyItem"]); + + expect(console.warn).not.toHaveBeenCalled(); + expect(response).toBeInstanceOf(Group); + expect(response._key).toBe("newGroup"); + expect(response.isPlaceholder).toBeFalsy(); + expect(response._value).toStrictEqual(["dummyItem"]); + expect(collection.groups["newGroup"]).toBe(response); + }); + + it("shouldn't create and add existing Group to Collection", () => { + const response = collection.createGroup("dummyGroup", ["dummyItem"]); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Group with the name 'dummyGroup' already exists!" + ); + expect(response).toBe(dummyGroup); + expect(collection.groups["dummyGroup"]).toBe(dummyGroup); + expect(dummyGroup.set).not.toHaveBeenCalled(); + }); + + it("should update existing placeholder Group of Collection", () => { + dummyGroup.isPlaceholder = true; + + const response = collection.createGroup("dummyGroup", ["dummyItem"]); + + expect(console.warn).not.toHaveBeenCalled(); + expect(response).toBe(dummyGroup); + expect(collection.groups["dummyGroup"]).toBe(dummyGroup); + expect(dummyGroup.set).toHaveBeenCalledWith(["dummyItem"], { + overwrite: true, + }); + }); + }); + + describe("getGroup function tests", () => { + let dummyGroup: Group; + + beforeEach(() => { + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + collection.groups = { + dummyGroup: dummyGroup, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Group (default config)", () => { + const response = collection.getGroup("dummyGroup"); + + expect(response).toBe(dummyGroup); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyGroup.observer + ); + }); + + it("shouldn't return and track not existing Group (default config)", () => { + const response = collection.getGroup("notExistingGroup"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("shouldn't return and track existing placeholder Group (default config)", () => { + dummyGroup.isPlaceholder = true; + + const response = collection.getGroup("dummyGroup"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("should return and track existing placeholder Group (config.notExisting = true)", () => { + dummyGroup.isPlaceholder = true; + + const response = collection.getGroup("dummyGroup", { + notExisting: true, + }); + + expect(response).toBe(dummyGroup); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyGroup.observer + ); + }); + }); + + describe("getGroupWithReference function tests", () => { + let dummyGroup: Group; + + beforeEach(() => { + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + collection.groups = { + dummyGroup: dummyGroup, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Group", () => { + const response = collection.getGroupWithReference("dummyGroup"); + + expect(response).toBe(dummyGroup); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyGroup.observer + ); + }); + + it("should return and track created reference Group if Group doesn't exist yet", () => { + const response = collection.getGroupWithReference("notExistingGroup"); + + expect(response).toBeInstanceOf(Group); + expect(response.isPlaceholder).toBeTruthy(); + expect(response._key).toBe("notExistingGroup"); + expect(collection.groups["notExistingGroup"]).toBe(response); + expect(ComputedTracker.tracked).toHaveBeenCalledWith(response.observer); + }); + }); + + describe("removeGroup function tests", () => { + let dummyGroup: Group; + + beforeEach(() => { + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + collection.groups = { + dummyGroup: dummyGroup, + }; + }); + + it("should remove existing Group", () => { + collection.removeGroup("dummyGroup"); + + expect(console.warn).not.toHaveBeenCalled(); + expect(collection.groups["dummyGroup"]).toBeUndefined(); + }); + + it("shouldn't remove not existing Group and print warning", () => { + collection.removeGroup("notExistingGroup"); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Group with the key/name 'notExistingGroup' doesn't exist!" + ); + }); + }); + + describe("createSelector function tests", () => { + let dummySelector: Selector; + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "dummyItem", name: "frank" }); + collection.data = { + dummyItem: dummyItem, + }; + + dummySelector = new Selector(collection, "dummyItem", { + key: "dummySelector", + }); + collection.selectors = { + dummySelector: dummySelector, + }; + + dummySelector.select = jest.fn(); + }); + + it("should create and add not existing Selector to Collection", () => { + const response = collection.createSelector("newSelector", "dummyItem"); + + expect(console.warn).not.toHaveBeenCalled(); + expect(response).toBeInstanceOf(Selector); + expect(response._key).toBe("newSelector"); + expect(response.isPlaceholder).toBeFalsy(); + expect(response._itemKey).toStrictEqual("dummyItem"); + expect(collection.selectors["newSelector"]).toBe(response); + }); + + it("shouldn't create and add existing Selector to Collection", () => { + const response = collection.createSelector( + "dummySelector", + "dummyItem" + ); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Selector with the name 'dummySelector' already exists!" + ); + expect(response).toBe(dummySelector); + expect(collection.selectors["dummySelector"]).toBe(dummySelector); + expect(dummySelector.select).not.toHaveBeenCalled(); + }); + + it("should update existing placeholder Selector of Collection", () => { + dummySelector.isPlaceholder = true; + + const response = collection.createSelector( + "dummySelector", + "dummyItem" + ); + + expect(console.warn).not.toHaveBeenCalled(); + expect(response).toBe(dummySelector); + expect(collection.selectors["dummySelector"]).toBe(dummySelector); + expect(dummySelector.select).toHaveBeenCalledWith("dummyItem", { + overwrite: true, + }); + }); + }); + + describe("getSelector function tests", () => { + let dummySelector: Selector; + + beforeEach(() => { + dummySelector = new Selector(collection, "dummyItem", { + key: "dummySelector", + }); + collection.selectors = { + dummySelector: dummySelector, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Selector (default config)", () => { + const response = collection.getSelector("dummySelector"); + + expect(response).toBe(dummySelector); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummySelector.observer + ); + }); + + it("shouldn't return and track not existing Selector (default config)", () => { + const response = collection.getSelector("notExistingSelector"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("shouldn't return and track existing placeholder Selector (default config)", () => { + dummySelector.isPlaceholder = true; + + const response = collection.getSelector("dummySelector"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("should return and track existing placeholder Selector (config.notExisting = true)", () => { + dummySelector.isPlaceholder = true; + + const response = collection.getSelector("dummySelector", { + notExisting: true, + }); + + expect(response).toBe(dummySelector); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummySelector.observer + ); + }); + }); + + describe("getSelectorWithReference function tests", () => { + let dummySelector: Selector; + + beforeEach(() => { + dummySelector = new Selector(collection, "dummyItem", { + key: "dummySelector", + }); + collection.selectors = { + dummySelector: dummySelector, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Selector", () => { + const response = collection.getSelectorWithReference("dummySelector"); + + expect(response).toBe(dummySelector); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummySelector.observer + ); + }); + + it("should return and track created reference Selector if Selector doesn't exist yet", () => { + const response = collection.getSelectorWithReference( + "notExistingSelector" + ); + + expect(response).toBeInstanceOf(Selector); + expect(response.isPlaceholder).toBeTruthy(); + expect(response._key).toBe("notExistingSelector"); + expect(collection.selectors["notExistingSelector"]).toBe(response); + expect(ComputedTracker.tracked).toHaveBeenCalledWith(response.observer); + }); + }); + + describe("removeSelector function tests", () => { + let dummySelector: Selector; + + beforeEach(() => { + dummySelector = new Selector(collection, "dummyItem", { + key: "dummySelector", + }); + collection.selectors = { + dummySelector: dummySelector, + }; + + dummySelector.unselect = jest.fn(); + }); + + it("should remove existing Selector", () => { + collection.removeSelector("dummySelector"); + + expect(console.warn).not.toHaveBeenCalled(); + expect(collection.selectors).not.toHaveProperty("dummySelector"); + expect(dummySelector.unselect).toHaveBeenCalled(); + }); + + it("shouldn't remove not existing Selector and print warning", () => { + collection.removeSelector("notExistingSelector"); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Selector with the key/name 'notExistingSelector' doesn't exist!" + ); + expect(collection.selectors).toHaveProperty("dummySelector"); + expect(dummySelector.unselect).not.toHaveBeenCalled(); + }); + }); + + describe("getItem function tests", () => { + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "1", name: "Jeff" }); + collection.data = { + ["1"]: dummyItem, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Item (default config)", () => { + const response = collection.getItem("1"); + + expect(response).toBe(dummyItem); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyItem.observer + ); + }); + + it("shouldn't return and track not existing Item (default config)", () => { + const response = collection.getItem("notExistingItem"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("shouldn't return and track existing placeholder Item (default config)", () => { + dummyItem.isPlaceholder = true; + + const response = collection.getItem("1"); + + expect(response).toBeUndefined(); + expect(ComputedTracker.tracked).not.toHaveBeenCalled(); + }); + + it("should return and track existing placeholder Item (config.notExisting = true)", () => { + dummyItem.isPlaceholder = true; + + const response = collection.getItem("1", { + notExisting: true, + }); + + expect(response).toBe(dummyItem); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyItem.observer + ); + }); + }); + + describe("getItemWithReference function tests", () => { + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "1", name: "Jeff" }); + collection.data = { + ["1"]: dummyItem, + }; + + ComputedTracker.tracked = jest.fn(); + }); + + it("should return and track existing Item", () => { + const response = collection.getItemWithReference("1"); + + expect(response).toBe(dummyItem); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + dummyItem.observer + ); + }); + + it("should return and track created reference Item if Item doesn't exist yet", () => { + const response = collection.getItemWithReference("notExistingItem"); + + expect(response).toBeInstanceOf(Item); + expect(response.isPlaceholder).toBeTruthy(); + expect(response._key).toBe("notExistingItem"); + expect(collection.data["notExistingItem"]).toBe(response); + expect(ComputedTracker.tracked).toHaveBeenCalledWith(response.observer); + }); + }); + + describe("getItemValue function tests", () => { + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "1", name: "Jeff" }); + collection.data = { + ["1"]: dummyItem, + }; + + jest.spyOn(collection, "getItem"); + }); + + it("should return existing Item Value (default config)", () => { + const response = collection.getItemValue("1"); + + expect(response).toBe(dummyItem._value); + expect(collection.getItem).toHaveBeenCalledWith("1", {}); + }); + + it("shouldn't return not existing Item Value (default config)", () => { + const response = collection.getItemValue("notExistingItem"); + + expect(response).toBeUndefined(); + expect(collection.getItem).toHaveBeenCalledWith("notExistingItem", {}); + }); + + it("shouldn't return existing placeholder Item Value (default config)", () => { + dummyItem.isPlaceholder = true; + + const response = collection.getItemValue("1"); + + expect(response).toBeUndefined(); + expect(collection.getItem).toHaveBeenCalledWith("1", {}); + }); + + it("should return existing placeholder Item Value (config.notExisting = true)", () => { + dummyItem.isPlaceholder = true; + + const response = collection.getItemValue("1", { + notExisting: true, + }); + + expect(response).toBe(dummyItem._value); + expect(collection.getItem).toHaveBeenCalledWith("1", { + notExisting: true, + }); + }); + }); + + describe("persist function tests", () => { + it("should create persistent with CollectionKey (default config)", () => { + collection.persist(); + + expect(collection.persistent).toBeInstanceOf(CollectionPersistent); + expect(CollectionPersistent).toHaveBeenCalledWith(collection, { + instantiate: true, + storageKeys: [], + key: collection._key, + }); + }); + + it("should create persistent with CollectionKey (specific config)", () => { + collection.persist({ + storageKeys: ["test1", "test2"], + instantiate: false, + }); + + expect(collection.persistent).toBeInstanceOf(CollectionPersistent); + expect(CollectionPersistent).toHaveBeenCalledWith(collection, { + instantiate: false, + storageKeys: ["test1", "test2"], + key: collection._key, + }); + }); + + it("should create persistent with passed Key (default config)", () => { + collection.persist("passedKey"); + + expect(collection.persistent).toBeInstanceOf(CollectionPersistent); + expect(CollectionPersistent).toHaveBeenCalledWith(collection, { + instantiate: true, + storageKeys: [], + key: "passedKey", + }); + }); + + it("should create persistent with passed Key (specific config)", () => { + collection.persist("passedKey", { + storageKeys: ["test1", "test2"], + instantiate: false, + }); + + expect(collection.persistent).toBeInstanceOf(CollectionPersistent); + expect(CollectionPersistent).toHaveBeenCalledWith(collection, { + instantiate: false, + storageKeys: ["test1", "test2"], + key: "passedKey", + }); + }); + + it("should overwrite existing persistent with a warning", () => { + collection.persistent = new CollectionPersistent(collection); + + collection.persist("newPersistentKey"); + + expect(collection.persistent).toBeInstanceOf(CollectionPersistent); + // expect(collection.persistent._key).toBe("newPersistentKey"); // Can not test because of Mocking Persistent + expect(CollectionPersistent).toHaveBeenCalledWith(collection, { + instantiate: true, + storageKeys: [], + key: "newPersistentKey", + }); + expect(console.warn).toHaveBeenCalledWith( + `Agile Warn: By persisting the Collection '${collection._key}' twice you overwrite the old Persistent Instance!` + ); + }); + }); + + describe("onLoad function tests", () => { + const dummyCallbackFunction = jest.fn(); + + it("should set onLoad function if Collection is persisted and shouldn't call it initially (collection.isPersisted = false)", () => { + collection.persistent = new CollectionPersistent(collection); + collection.isPersisted = false; + + collection.onLoad(dummyCallbackFunction); + + expect(collection.persistent.onLoad).toBe(dummyCallbackFunction); + expect(dummyCallbackFunction).not.toHaveBeenCalled(); + }); + + it("should set onLoad function if Collection is persisted and should call it initially (collection.isPersisted = true)", () => { + collection.persistent = new CollectionPersistent(collection); + collection.isPersisted = true; + + collection.onLoad(dummyCallbackFunction); + + expect(collection.persistent.onLoad).toBe(dummyCallbackFunction); + expect(dummyCallbackFunction).toHaveBeenCalledWith(true); + }); + + it("shouldn't set onLoad function if Collection isn't persisted and should drop a error", () => { + collection.onLoad(dummyCallbackFunction); + + expect(dummyCallbackFunction).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Please make sure you persist the Collection 'collectionKey' before using the 'onLoad' function!" + ); + }); + }); + + describe("getGroupCount function tests", () => { + beforeEach(() => { + collection.groups = { + 1: "x" as any, + 2: "y" as any, + 10: "z" as any, + }; + }); + + it("should return count of registered Groups", () => { + expect(collection.getGroupCount()).toBe(3); + }); + }); + + describe("getSelectorCount function tests", () => { + beforeEach(() => { + collection.selectors = { + 1: "x" as any, + 2: "y" as any, + 10: "z" as any, + }; + }); + + it("should return count of registered Selectors", () => { + expect(collection.getSelectorCount()).toBe(3); + }); + }); + + describe("reset function tests", () => { + let dummyGroup: Group; + let dummySelector: Selector; + let dummyItem: Item; + + beforeEach(() => { + dummyItem = new Item(collection, { id: "dummyItem", name: "frank" }); + collection.data = { + dummyItem: dummyItem, + }; + dummyGroup = new Group(collection, [], { key: "dummyGroup" }); + collection.groups = { + dummyGroup: dummyGroup, + }; + dummySelector = new Selector(collection, "dummyItem", { + key: "dummySelector", + }); + collection.selectors = { + dummySelector: dummySelector, + }; + + dummyGroup.reset = jest.fn(); + dummySelector.reset = jest.fn(); + }); + + it("should reset Collection and its Selectors and Groups", () => { + collection.reset(); + + expect(collection.data).toStrictEqual({}); + expect(collection.size).toBe(0); + expect(dummySelector.reset).toHaveBeenCalled(); + expect(dummyGroup.reset).toHaveBeenCalled(); + }); + }); + + describe("put function tests", () => { + let dummyGroup1: Group; + let dummyGroup2: Group; + + beforeEach(() => { + dummyGroup1 = new Group(collection, [], { key: "dummyGroup1" }); + dummyGroup2 = new Group(collection, [], { key: "dummyGroup2" }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }; + + dummyGroup1.add = jest.fn(); + dummyGroup2.add = jest.fn(); + }); + + it("should add passed itemKey to passed Group (default config)", () => { + collection.put("1", "dummyGroup1"); + + expect(dummyGroup1.add).toHaveBeenCalledWith(["1"], {}); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + }); + + it("should add passed itemKey to passed Group (specific config)", () => { + collection.put("1", "dummyGroup1", { + overwrite: true, + method: "push", + }); + + expect(dummyGroup1.add).toHaveBeenCalledWith(["1"], { + overwrite: true, + method: "push", + }); + expect(dummyGroup2.add).not.toHaveBeenCalled(); + }); + + it("should add passed itemKeys to passed Groups (default config)", () => { + collection.put( + ["1", "2", "3"], + ["dummyGroup1", "notExistingGroup", "dummyGroup2"] + ); + + expect(dummyGroup1.add).toHaveBeenCalledWith(["1", "2", "3"], {}); + expect(dummyGroup2.add).toHaveBeenCalledWith(["1", "2", "3"], {}); + }); + }); + + describe("updateItemKey function tests", () => { + let dummySelector1: Selector; + let dummySelector2: Selector; + let dummySelector3: Selector; + let dummyGroup1: Group; + let dummyGroup2: Group; + let dummyItem1: Item; + let dummyItem2: Item; + + beforeEach(() => { + dummyItem1 = new Item(collection, { id: "dummyItem1", name: "Jeff" }); + dummyItem1.persistent = new StatePersistent(dummyItem1); + dummyItem2 = new Item(collection, { id: "dummyItem2", name: "Hans" }); + dummyItem2.persistent = new StatePersistent(dummyItem2); + collection.data = { + dummyItem1: dummyItem1, + dummyItem2: dummyItem2, + }; + + dummyGroup1 = new Group(collection, ["dummyItem1", "dummyItem2"], { + key: "dummyGroup1", + }); + dummyGroup2 = new Group(collection, ["dummyItem2"], { + key: "dummyGroup2", + }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }; + + dummySelector1 = new Selector(collection, "dummyItem1", { + key: "dummySelector1", + }); + dummySelector2 = new Selector(collection, "dummyItem2", { + key: "dummySelector2", + }); + dummySelector3 = new Selector(collection, "newDummyItem", { + key: "dummySelector3", + }); + collection.selectors = { + dummySelector1: dummySelector1, + dummySelector2: dummySelector2, + dummySelector3: dummySelector3, + }; + + dummyItem1.setKey = jest.fn(); + dummyItem2.setKey = jest.fn(); + dummyItem1.persistent.setKey = jest.fn(); + dummyItem2.persistent.setKey = jest.fn(); + + dummyGroup1.replace = jest.fn(); + dummyGroup2.replace = jest.fn(); + + dummySelector1.select = jest.fn(); + dummySelector2.select = jest.fn(); + dummySelector3.select = jest.fn(); + }); + + it("should update ItemKey in Collection, Selectors and Groups (default config)", () => { + const response = collection.updateItemKey("dummyItem1", "newDummyItem"); + + expect(response).toBeTruthy(); + + expect(dummyItem1.setKey).toHaveBeenCalledWith("newDummyItem"); + expect(dummyItem2.setKey).not.toHaveBeenCalled(); + expect(dummyItem1.persistent.setKey).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "newDummyItem", + collection._key + ) + ); + expect(dummyItem2.persistent.setKey).not.toHaveBeenCalled(); + + expect(dummyGroup1.replace).toHaveBeenCalledWith( + "dummyItem1", + "newDummyItem", + { + background: false, + } + ); + expect(dummyGroup2.replace).not.toHaveBeenCalled(); + + expect(dummySelector1.select).toHaveBeenCalledWith("newDummyItem", { + background: false, + }); + expect(dummySelector2.select).not.toHaveBeenCalled(); + expect(dummySelector3.select).toHaveBeenCalledWith("newDummyItem", { + force: true, + background: false, + }); + }); + + it("should update ItemKey in Collection, Selectors and Groups (specific config)", () => { + const response = collection.updateItemKey( + "dummyItem1", + "newDummyItem", + { + background: true, + } + ); + + expect(response).toBeTruthy(); + + expect(dummyItem1.setKey).toHaveBeenCalledWith("newDummyItem"); + expect(dummyItem1.persistent.setKey).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "newDummyItem", + collection._key + ) + ); + + expect(dummyGroup1.replace).toHaveBeenCalledWith( + "dummyItem1", + "newDummyItem", + { + background: true, + } + ); + + expect(dummySelector1.select).toHaveBeenCalledWith("newDummyItem", { + background: true, + }); + expect(dummySelector3.select).toHaveBeenCalledWith("newDummyItem", { + force: true, + background: true, + }); + }); + + it("should update ItemKey in Collection, dummy Selectors and dummy Groups (default config)", () => { + dummyGroup1.isPlaceholder = true; + dummySelector1.isPlaceholder = true; + + const response = collection.updateItemKey("dummyItem1", "newDummyItem"); + + expect(response).toBeTruthy(); + + expect(dummyItem1.setKey).toHaveBeenCalledWith("newDummyItem"); + expect(dummyItem1.persistent.setKey).toHaveBeenCalledWith( + CollectionPersistent.getItemStorageKey( + "newDummyItem", + collection._key + ) + ); + + expect(dummyGroup1.replace).toHaveBeenCalledWith( + "dummyItem1", + "newDummyItem", + { + background: false, + } + ); + + expect(dummySelector1.select).toHaveBeenCalledWith("newDummyItem", { + background: false, + }); + expect(dummySelector3.select).toHaveBeenCalledWith("newDummyItem", { + force: true, + background: false, + }); + }); + + it("shouldn't update ItemKey of Item that doesn't exist (default config)", () => { + const response = collection.updateItemKey( + "notExistingItem", + "newDummyItem" + ); + + expect(response).toBeFalsy(); + }); + + it("shouldn't update ItemKey if it stayed the same (default config)", () => { + const response = collection.updateItemKey("dummyItem1", "dummyItem1"); + + expect(response).toBeFalsy(); + }); + }); + + describe("getGroupKeysThatHaveItemKey function tests", () => { + let dummyGroup1: Group; + let dummyGroup2: Group; + let dummyGroup3: Group; + + beforeEach(() => { + dummyGroup1 = new Group( + collection, + ["dummyItem1", "dummyItem2", "dummyItem3"], + { + key: "dummyGroup1", + } + ); + dummyGroup2 = new Group(collection, ["dummyItem2", "dummyItem3"], { + key: "dummyGroup2", + }); + dummyGroup3 = new Group(collection, ["dummyItem3"], { + key: "dummyGroup3", + }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + dummyGroup3: dummyGroup3, + }; + }); + + it("should return groupKeys that contain ItemKey", () => { + expect( + collection.getGroupKeysThatHaveItemKey("unknownItem") + ).toStrictEqual([]); + expect( + collection.getGroupKeysThatHaveItemKey("dummyItem1") + ).toStrictEqual(["dummyGroup1"]); + expect( + collection.getGroupKeysThatHaveItemKey("dummyItem2") + ).toStrictEqual(["dummyGroup1", "dummyGroup2"]); + expect( + collection.getGroupKeysThatHaveItemKey("dummyItem3") + ).toStrictEqual(["dummyGroup1", "dummyGroup2", "dummyGroup3"]); + }); + }); + + describe("remove function tests", () => { + beforeEach(() => { + collection.removeFromGroups = jest.fn(); + collection.removeItems = jest.fn(); + }); + + it("should remove Items from Group", () => { + collection + .remove(["test1", "test2"]) + .fromGroups(["testGroup1", "testGroup2"]); + + expect(collection.removeFromGroups).toHaveBeenCalledWith( + ["test1", "test2"], + ["testGroup1", "testGroup2"] + ); + expect(collection.removeItems).not.toHaveBeenCalled(); + }); + + it("should remove Items from everywhere", () => { + collection.remove(["test1", "test2"]).everywhere(); + + expect(collection.removeFromGroups).not.toHaveBeenCalled(); + expect(collection.removeItems).toHaveBeenCalledWith(["test1", "test2"]); + }); + }); + + describe("removeFromGroups function tests", () => { + let dummyGroup1: Group; + let dummyGroup2: Group; + let dummyGroup3: Group; + + beforeEach(() => { + dummyGroup1 = new Group( + collection, + ["dummyItem1", "dummyItem2", "dummyItem3", "unknownItem"], + { + key: "dummyGroup1", + } + ); + dummyGroup2 = new Group( + collection, + ["dummyItem2", "dummyItem3", "unknownItem"], + { + key: "dummyGroup2", + } + ); + dummyGroup3 = new Group(collection, ["dummyItem3", "unknownItem"], { + key: "dummyGroup3", + }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + dummyGroup3: dummyGroup3, + }; + + collection.removeItems = jest.fn(); + dummyGroup1.remove = jest.fn(); + dummyGroup2.remove = jest.fn(); + dummyGroup3.remove = jest.fn(); + }); + + it("should remove ItemKey from Group", () => { + collection.removeFromGroups("dummyItem2", "dummyGroup1"); + + expect(collection.removeItems).not.toHaveBeenCalled(); + expect(dummyGroup1.remove).toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup2.remove).not.toHaveBeenCalled(); + expect(dummyGroup3.remove).not.toHaveBeenCalled(); + }); + + it("should remove ItemKeys from Groups", () => { + collection.removeFromGroups( + ["dummyItem2", "dummyItem3"], + ["dummyGroup2", "dummyGroup3"] + ); + + expect(collection.removeItems).not.toHaveBeenCalled(); + expect(dummyGroup1.remove).not.toHaveBeenCalled(); + expect(dummyGroup2.remove).toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup2.remove).toHaveBeenCalledWith("dummyItem3"); + expect(dummyGroup3.remove).not.toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup3.remove).toHaveBeenCalledWith("dummyItem3"); + }); + + it("should remove Item from Collection if it got removed from all Groups in which it was in", () => { + collection.removeFromGroups("dummyItem2", [ + "dummyGroup2", + "dummyGroup1", + ]); + + expect(collection.removeItems).toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup1.remove).toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup2.remove).toHaveBeenCalledWith("dummyItem2"); + }); + }); + + describe("removeItems function test", () => { + let dummySelector1: Selector; + let dummySelector2: Selector; + let dummyGroup1: Group; + let dummyGroup2: Group; + let dummyItem1: Item; + let dummyItem2: Item; + + beforeEach(() => { + dummyItem1 = new Item(collection, { id: "dummyItem1", name: "Jeff" }); + dummyItem1.persistent = new StatePersistent(dummyItem1); + dummyItem2 = new Item(collection, { id: "dummyItem2", name: "Hans" }); + dummyItem2.persistent = new StatePersistent(dummyItem2); + collection.data = { + dummyItem1: dummyItem1, + dummyItem2: dummyItem2, + }; + collection.size = 2; + + dummyGroup1 = new Group(collection, ["dummyItem1", "dummyItem2"], { + key: "dummyGroup1", + }); + dummyGroup2 = new Group(collection, ["dummyItem2"], { + key: "dummyGroup2", + }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }; + + dummySelector1 = new Selector(collection, "dummyItem1", { + key: "dummySelector1", + }); + dummySelector2 = new Selector(collection, "dummyItem2", { + key: "dummySelector2", + }); + collection.selectors = { + dummySelector1: dummySelector1, + dummySelector2: dummySelector2, + }; + + dummyItem1.persistent.removePersistedValue = jest.fn(); + dummyItem2.persistent.removePersistedValue = jest.fn(); + + dummyGroup1.remove = jest.fn(); + dummyGroup2.remove = jest.fn(); + + dummySelector1.select = jest.fn(); + dummySelector2.select = jest.fn(); + }); + + it("should remove Item from Collection, Groups and Selectors", () => { + collection.removeItems("dummyItem1"); + + expect(collection.data).not.toHaveProperty("dummyItem1"); + expect(collection.data).toHaveProperty("dummyItem2"); + expect(collection.size).toBe(1); + + expect(dummyItem1.persistent.removePersistedValue).toHaveBeenCalled(); + expect( + dummyItem2.persistent.removePersistedValue + ).not.toHaveBeenCalled(); + + expect(dummyGroup1.remove).toHaveBeenCalledWith("dummyItem1"); + expect(dummyGroup2.remove).not.toHaveBeenCalled(); + + expect(dummySelector1.select).toHaveBeenCalledWith("dummyItem1", { + force: true, + }); + expect(dummySelector2.select).not.toHaveBeenCalled(); + }); + + it("should remove Items from Collection, Groups and Selectors", () => { + collection.removeItems(["dummyItem1", "dummyItem2", "notExistingItem"]); + + expect(collection.data).not.toHaveProperty("dummyItem1"); + expect(collection.data).not.toHaveProperty("dummyItem2"); + expect(collection.size).toBe(0); + + expect(dummyItem1.persistent.removePersistedValue).toHaveBeenCalled(); + expect(dummyItem2.persistent.removePersistedValue).toHaveBeenCalled(); + + expect(dummyGroup1.remove).toHaveBeenCalledWith("dummyItem1"); + expect(dummyGroup1.remove).toHaveBeenCalledWith("dummyItem2"); + expect(dummyGroup2.remove).not.toHaveBeenCalledWith("dummyItem1"); + expect(dummyGroup2.remove).toHaveBeenCalledWith("dummyItem2"); + + expect(dummySelector1.select).toHaveBeenCalledWith("dummyItem1", { + force: true, + }); + expect(dummySelector2.select).toHaveBeenCalledWith("dummyItem2", { + force: true, + }); + }); + }); + + describe("setData function tests", () => { + let dummyItem1: Item; + + beforeEach(() => { + dummyItem1 = new Item(collection, { id: "dummyItem1", name: "Jeff" }); + collection.data = { + dummyItem1: dummyItem1, + }; + collection.size = 1; + + collection.rebuildGroupsThatIncludeItemKey = jest.fn(); + dummyItem1.patch = jest.fn(); + dummyItem1.set = jest.fn(); + }); + + it("should create new Item out of valid Data, rebuild Groups and increase size (default config)", () => { + const response = collection.setData({ id: "dummyItem2", name: "Hans" }); + + expect(response).toBeTruthy(); + expect(collection.data).toHaveProperty("dummyItem1"); + expect(collection.data).toHaveProperty("dummyItem2"); + expect(collection.data["dummyItem2"]).toBeInstanceOf(Item); + expect(collection.data["dummyItem2"]._value).toStrictEqual({ + id: "dummyItem2", + name: "Hans", + }); + expect(collection.size).toBe(2); + expect(collection.rebuildGroupsThatIncludeItemKey).toHaveBeenCalledWith( + "dummyItem2" + ); + }); + + it("shouldn't create new Item if passed Data is no valid Object", () => { + const response = collection.setData("noObject" as any); + + expect(console.error).toHaveBeenCalledWith( + `Agile Error: Item Data of Collection '${collection._key}' has to be an valid Object!` + ); + + expect(response).toBeFalsy(); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + }); + + it("shouldn't create new Item if passed Data has no primaryKey", () => { + const response = collection.setData({ name: "Frank" } as any); + + expect(console.error).toHaveBeenCalledWith( + `Agile Error: Collection '${collection._key}' Item Data has to contain a primaryKey property called '${collection.config.primaryKey}'!` + ); + + expect(response).toBeFalsy(); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + }); + + it("should update Item with valid Data, shouldn't rebuild Groups and shouldn't increase size (default config)", () => { + const response = collection.setData({ + id: "dummyItem1", + name: "Dieter", + }); + + expect(response).toBeTruthy(); + + expect(collection.data).toHaveProperty("dummyItem1"); + expect(collection.data["dummyItem1"]).toBeInstanceOf(Item); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + + expect(dummyItem1.set).toHaveBeenCalledWith( + { id: "dummyItem1", name: "Dieter" }, + { background: false } + ); + expect(dummyItem1.patch).not.toHaveBeenCalled(); + }); + + it("should update Item with valid Data, shouldn't rebuild Groups and shouldn't increase size (config.background = true)", () => { + const response = collection.setData( + { + id: "dummyItem1", + name: "Dieter", + }, + { background: true } + ); + + expect(response).toBeTruthy(); + + expect(collection.data).toHaveProperty("dummyItem1"); + expect(collection.data["dummyItem1"]).toBeInstanceOf(Item); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + + expect(dummyItem1.set).toHaveBeenCalledWith( + { id: "dummyItem1", name: "Dieter" }, + { background: true } + ); + expect(dummyItem1.patch).not.toHaveBeenCalled(); + }); + + it("should update Item with valid Data, shouldn't rebuild Groups and shouldn't increase size (config.patch = true, background: true)", () => { + const response = collection.setData( + { + id: "dummyItem1", + name: "Dieter", + }, + { patch: true, background: true } + ); + + expect(response).toBeTruthy(); + + expect(collection.data).toHaveProperty("dummyItem1"); + expect(collection.data["dummyItem1"]).toBeInstanceOf(Item); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + + expect(dummyItem1.set).not.toHaveBeenCalled(); + expect(dummyItem1.patch).toHaveBeenCalledWith( + { id: "dummyItem1", name: "Dieter" }, + { background: true } + ); + }); + + it("should update placeholder Item with valid Data, shouldn't rebuild Groups and should increase size (default config)", () => { + dummyItem1.isPlaceholder = true; + collection.size = 0; + + const response = collection.setData({ + id: "dummyItem1", + name: "Dieter", + }); + + expect(response).toBeTruthy(); + + expect(collection.data).toHaveProperty("dummyItem1"); + expect(collection.data["dummyItem1"]).toBeInstanceOf(Item); + expect(collection.size).toBe(1); + expect( + collection.rebuildGroupsThatIncludeItemKey + ).not.toHaveBeenCalled(); + + expect(dummyItem1.set).toHaveBeenCalledWith( + { id: "dummyItem1", name: "Dieter" }, + { background: false } + ); + }); + }); + + describe("rebuildGroupsThatIncludeItemKey function tests", () => { + let dummyGroup1: Group; + let dummyGroup2: Group; + + beforeEach(() => { + dummyGroup1 = new Group(collection, ["dummyItem1", "dummyItem2"], { + key: "dummyGroup1", + }); + dummyGroup2 = new Group(collection, ["dummyItem2"], { + key: "dummyGroup2", + }); + collection.groups = { + dummyGroup1: dummyGroup1, + dummyGroup2: dummyGroup2, + }; + + dummyGroup1.ingest = jest.fn(); + dummyGroup2.ingest = jest.fn(); + }); + + it("should call ingest on each Group that includes the passed ItemKey (default config)", () => { + collection.rebuildGroupsThatIncludeItemKey("dummyItem1"); + + expect(dummyGroup1.ingest).toHaveBeenCalledWith({ + background: false, + force: true, + sideEffects: true, + storage: false, + }); + expect(dummyGroup2.ingest).not.toHaveBeenCalled(); + }); + + it("should call ingest on each Group that includes the passed ItemKey (specific config)", () => { + collection.rebuildGroupsThatIncludeItemKey("dummyItem2", { + background: true, + sideEffects: false, + }); + + expect(dummyGroup1.ingest).toHaveBeenCalledWith({ + background: true, + force: true, + sideEffects: false, + storage: false, + }); + expect(dummyGroup2.ingest).toHaveBeenCalledWith({ + background: true, + force: true, + sideEffects: false, + storage: false, + }); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/collection/group.test.ts b/packages/core/tests/unit/collection/group.test.ts new file mode 100644 index 00000000..5487bd5b --- /dev/null +++ b/packages/core/tests/unit/collection/group.test.ts @@ -0,0 +1,510 @@ +import { + Group, + Agile, + Collection, + StateObserver, + ComputedTracker, + Item, + State, + CollectionPersistent, +} from "../../../src"; + +describe("Group Tests", () => { + interface ItemInterface { + id: string; + name: string; + } + let dummyAgile: Agile; + let dummyCollection: Collection; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyAgile = new Agile({ localStorage: false }); + dummyCollection = new Collection(dummyAgile, { + key: "dummyCollection", + }); + + jest.spyOn(Group.prototype, "rebuild"); + jest.spyOn(Group.prototype, "addSideEffect"); + console.error = jest.fn(); + console.warn = jest.fn(); + }); + + it("should create Group with no initialItems (default config)", () => { + // Overwrite methods once to not call it + jest.spyOn(Group.prototype, "rebuild").mockReturnValueOnce(undefined); + jest.spyOn(Group.prototype, "addSideEffect").mockReturnValueOnce(undefined); + + const group = new Group(dummyCollection); + + expect(group.collection()).toBe(dummyCollection); + expect(group._output).toStrictEqual([]); + expect(group._items).toStrictEqual([]); + expect(group.notFoundItemKeys).toStrictEqual([]); + + expect(group._key).toBeUndefined(); + expect(group.valueType).toBeUndefined(); + expect(group.isSet).toBeFalsy(); + expect(group.isPlaceholder).toBeFalsy(); + expect(group.initialStateValue).toStrictEqual([]); + expect(group._value).toStrictEqual([]); + expect(group.previousStateValue).toStrictEqual([]); + expect(group.nextStateValue).toStrictEqual([]); + expect(group.observer).toBeInstanceOf(StateObserver); + expect(group.observer.deps.size).toBe(0); + expect(group.observer._key).toBeUndefined(); + expect(group.sideEffects).toStrictEqual({}); + expect(group.computeMethod).toBeUndefined(); + expect(group.isPersisted).toBeFalsy(); + expect(group.persistent).toBeUndefined(); + expect(group.watchers).toStrictEqual({}); + }); + + it("should create Group with no initialItems (specific config)", () => { + // Overwrite methods once to not call it + jest.spyOn(Group.prototype, "rebuild").mockReturnValueOnce(undefined); + jest.spyOn(Group.prototype, "addSideEffect").mockReturnValueOnce(undefined); + + const group = new Group(dummyCollection, [], { + key: "dummyKey", + isPlaceholder: true, + }); + + expect(group.collection()).toBe(dummyCollection); + expect(group._output).toStrictEqual([]); + expect(group._items).toStrictEqual([]); + expect(group.notFoundItemKeys).toStrictEqual([]); + + expect(group._key).toBe("dummyKey"); + expect(group.valueType).toBeUndefined(); + expect(group.isSet).toBeFalsy(); + expect(group.isPlaceholder).toBeTruthy(); + expect(group.initialStateValue).toStrictEqual([]); + expect(group._value).toStrictEqual([]); + expect(group.previousStateValue).toStrictEqual([]); + expect(group.nextStateValue).toStrictEqual([]); + expect(group.observer).toBeInstanceOf(StateObserver); + expect(group.observer.deps.size).toBe(0); + expect(group.observer._key).toBe("dummyKey"); + expect(group.sideEffects).toStrictEqual({}); + expect(group.computeMethod).toBeUndefined(); + expect(group.isPersisted).toBeFalsy(); + expect(group.persistent).toBeUndefined(); + expect(group.watchers).toStrictEqual({}); + }); + + it("should create Group with initialItems (default config)", () => { + // Overwrite methods once to not call it + jest.spyOn(Group.prototype, "rebuild").mockReturnValueOnce(undefined); + jest.spyOn(Group.prototype, "addSideEffect").mockReturnValueOnce(undefined); + + const group = new Group(dummyCollection, ["test1", "test2", "test3"]); + + expect(group.collection()).toBe(dummyCollection); + expect(group._output).toStrictEqual([]); + expect(group._items).toStrictEqual([]); + expect(group.notFoundItemKeys).toStrictEqual([]); + + expect(group._key).toBeUndefined(); + expect(group.valueType).toBeUndefined(); + expect(group.isSet).toBeFalsy(); + expect(group.isPlaceholder).toBeFalsy(); + expect(group.initialStateValue).toStrictEqual(["test1", "test2", "test3"]); + expect(group._value).toStrictEqual(["test1", "test2", "test3"]); + expect(group.previousStateValue).toStrictEqual(["test1", "test2", "test3"]); + expect(group.nextStateValue).toStrictEqual(["test1", "test2", "test3"]); + expect(group.observer).toBeInstanceOf(StateObserver); + expect(group.observer.deps.size).toBe(0); + expect(group.observer._key).toBeUndefined(); + expect(group.sideEffects).toStrictEqual({}); + expect(group.computeMethod).toBeUndefined(); + expect(group.isPersisted).toBeFalsy(); + expect(group.persistent).toBeUndefined(); + expect(group.watchers).toStrictEqual({}); + }); + + describe("Group Function Tests", () => { + let group: Group; + let dummyItem1: Item; + let dummyItem2: Item; + let dummyItem3: Item; + + beforeEach(() => { + group = new Group(dummyCollection, [], { + key: "groupKey", + }); + dummyCollection.collect({ id: "dummyItem1Key", name: "coolName" }); + dummyCollection.collect({ id: "dummyItem2Key", name: "coolName" }); + dummyItem1 = dummyCollection.getItem("dummyItem1Key"); + dummyItem2 = dummyCollection.getItem("dummyItem2Key"); + dummyItem3 = new Item(dummyCollection, { + id: "dummyItem3Key", + name: "coolName", + }); + }); + + describe("output get function tests", () => { + beforeEach(() => { + jest.spyOn(ComputedTracker, "tracked"); + }); + + it("should return output of Group and call ComputedTracker.tracked", () => { + group._output = [ + { id: "1", name: "Frank" }, + { id: "2", name: "Hans" }, + ]; + + const response = group.output; + + expect(response).toStrictEqual([ + { id: "1", name: "Frank" }, + { id: "2", name: "Hans" }, + ]); + expect(ComputedTracker.tracked).toHaveBeenCalledWith(group.observer); + }); + }); + + describe("output set function tests", () => { + it("should set output to passed value", () => { + group.output = [ + { id: "12", name: "Hans der 3" }, + { id: "99", name: "Frank" }, + ]; + + expect(group._output).toStrictEqual([ + { id: "12", name: "Hans der 3" }, + { id: "99", name: "Frank" }, + ]); + }); + }); + + describe("item get function tests", () => { + beforeEach(() => { + jest.spyOn(ComputedTracker, "tracked"); + }); + + it("should return items of Group and call ComputedTracker.tracked", () => { + group._items = [() => dummyItem1, () => dummyItem2]; + + const response = group.items; + + expect(response).toStrictEqual([dummyItem1, dummyItem2]); + expect(ComputedTracker.tracked).toHaveBeenCalledWith(group.observer); + }); + }); + + describe("item set function tests", () => { + it("should set items to passed value", () => { + group.items = [dummyItem1, dummyItem2]; + + expect(group._items.length).toBe(2); + expect(group._items[0]()).toBe(dummyItem1); + expect(group._items[1]()).toBe(dummyItem2); + }); + }); + + describe("has function tests", () => { + beforeEach(() => { + group._value = ["test1", "test2"]; + }); + + it("should return true if group contains ItemKey", () => { + expect(group.has("test1")).toBeTruthy(); + }); + + it("should return false if group doesn't contain ItemKey", () => { + expect(group.has("notExistingKey")).toBeFalsy(); + }); + }); + + describe("size function tests", () => { + it("should return size of Group", () => { + group._value = ["test1", "test2"]; + + expect(group.size).toBe(2); + }); + }); + + describe("remove function tests", () => { + beforeEach(() => { + group.nextStateValue = [ + "dummyItem1Key", + "dummyItem2Key", + "dummyItem3Key", + ]; + group.set = jest.fn(); + }); + + it("should remove Item from Group not in background (default config)", () => { + group.remove("dummyItem1Key"); + + expect(console.error).not.toHaveBeenCalled(); + expect(group.set).toHaveBeenCalledWith( + ["dummyItem2Key", "dummyItem3Key"], + { background: false } + ); + }); + + it("should remove Item from Group in background (config.background = true)", () => { + group.remove("dummyItem1Key", { background: true }); + + expect(console.error).not.toHaveBeenCalled(); + expect(group.set).toHaveBeenCalledWith( + ["dummyItem2Key", "dummyItem3Key"], + { background: true } + ); + }); + + it("shouldn't remove not existing Item from Group (default config)", () => { + group.remove("notExistingKey"); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Couldn't find ItemKey 'notExistingKey' in Group 'groupKey'!" + ); + expect(group.set).not.toHaveBeenCalled(); + }); + + it("should remove Item from Group that doesn't exist in Collection in background (default config)", () => { + group.remove("dummyItem3Key"); + + expect(console.error).not.toHaveBeenCalled(); + expect(group.set).toHaveBeenCalledWith( + ["dummyItem1Key", "dummyItem2Key"], + { background: true } + ); + }); + + it("should remove Items from Group not in background (default config)", () => { + group.remove(["dummyItem1Key", "notExistingItemKey", "dummyItem3Key"]); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Couldn't find ItemKey 'notExistingItemKey' in Group 'groupKey'!" + ); + expect(group.set).toHaveBeenCalledWith(["dummyItem2Key"], { + background: false, + }); + }); + + it("should remove Items from Group in background if passing not existing Item and Item that doesn't exist in Collection (default config)", () => { + group.remove(["notExistingItemKey", "dummyItem3Key"]); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Couldn't find ItemKey 'notExistingItemKey' in Group 'groupKey'!" + ); + expect(group.set).toHaveBeenCalledWith( + ["dummyItem1Key", "dummyItem2Key"], + { + background: true, + } + ); + }); + }); + + describe("add function tests", () => { + beforeEach(() => { + group.nextStateValue = ["placeholder", "dummyItem1Key", "placeholder"]; + group.set = jest.fn(); + }); + + it("should add Item to Group at the end not in background (default config)", () => { + group.add("dummyItem2Key"); + + expect(group.set).toHaveBeenCalledWith( + ["placeholder", "dummyItem1Key", "placeholder", "dummyItem2Key"], + { background: false } + ); + }); + + it("should add Item to Group at the beginning not in background (config.method = 'unshift')", () => { + group.add("dummyItem2Key", { method: "unshift" }); + + expect(group.set).toHaveBeenCalledWith( + ["dummyItem2Key", "placeholder", "dummyItem1Key", "placeholder"], + { background: false } + ); + }); + + it("should add Item to Group at the end in background (config.background = true)", () => { + group.add("dummyItem2Key", { background: true }); + + expect(group.set).toHaveBeenCalledWith( + ["placeholder", "dummyItem1Key", "placeholder", "dummyItem2Key"], + { background: true } + ); + }); + + it("should add Item to Group at the end that doesn't exist in Collection in background (default config)", () => { + group.add("dummyItem3Key"); + + expect(group.set).toHaveBeenCalledWith( + ["placeholder", "dummyItem1Key", "placeholder", "dummyItem3Key"], + { background: true } + ); + }); + + it("shouldn't add existing Item to Group again (default config)", () => { + group.add("dummyItem1Key"); + + expect(group.set).not.toHaveBeenCalled(); + }); + + it("should remove existingItem and add it again at the end to the Group not in background (config.overwrite = true)", () => { + group.add("dummyItem1Key", { overwrite: true }); + + expect(group.set).toHaveBeenCalledWith( + ["placeholder", "placeholder", "dummyItem1Key"], + { background: false } + ); + }); + + it("should add Items to Group at the end not in background (default config)", () => { + group.add(["dummyItem1Key", "dummyItem2Key", "dummyItem3Key"]); + + expect(group.set).toHaveBeenCalledWith( + [ + "placeholder", + "dummyItem1Key", + "placeholder", + "dummyItem2Key", + "dummyItem3Key", + ], + { background: false } + ); + }); + + it("should add Items toGroup at the end in background if passing existing Item and in Collection not existing Item (default config)", () => { + group.add(["dummyItem1Key", "dummyItem3Key"]); + + expect(group.set).toHaveBeenCalledWith( + ["placeholder", "dummyItem1Key", "placeholder", "dummyItem3Key"], + { background: true } + ); + }); + }); + + describe("replace function tests", () => { + beforeEach(() => { + group._value = [1, 2, 3, 4, 5, 6]; + + group.set = jest.fn(); + }); + + it("should replace oldItemKey with new ItemKey (default config)", () => { + group.replace(4, 20); + + expect(group.set).toHaveBeenCalledWith([1, 2, 3, 20, 5, 6], {}); + }); + + it("should replace oldItemKey with new ItemKey (specific config)", () => { + group.replace(2, 20, { + storage: true, + sideEffects: false, + }); + + expect(group.set).toHaveBeenCalledWith([1, 20, 3, 4, 5, 6], { + storage: true, + sideEffects: false, + }); + }); + }); + + describe("persist function tests", () => { + beforeEach(() => { + jest.spyOn(State.prototype, "persist"); + }); + + it("should persist Group with GroupKey (default config)", () => { + group.persist(); + + expect(State.prototype.persist).toHaveBeenCalledWith(group._key, { + instantiate: true, + storageKeys: [], + }); + }); + + it("should persist Group with GroupKey (specific config)", () => { + group.persist({ instantiate: false, storageKeys: ["test1", "test2"] }); + + expect(State.prototype.persist).toHaveBeenCalledWith(group._key, { + instantiate: false, + storageKeys: ["test1", "test2"], + }); + }); + + it("should persist Group with passed Key (default config)", () => { + group.persist("dummyKey"); + + expect(State.prototype.persist).toHaveBeenCalledWith("dummyKey", { + instantiate: true, + storageKeys: [], + }); + }); + + it("should persist Group with passed Key (specific config)", () => { + group.persist("dummyKey", { + instantiate: false, + storageKeys: ["test1", "test2"], + }); + + expect(State.prototype.persist).toHaveBeenCalledWith("dummyKey", { + instantiate: false, + storageKeys: ["test1", "test2"], + }); + }); + + it("should persist Group with formatted GroupKey (config.followCollectionPersistKeyPattern)", () => { + group.persist({ followCollectionPersistKeyPattern: true }); + + expect(State.prototype.persist).toHaveBeenCalledWith( + CollectionPersistent.getGroupStorageKey( + group._key, + dummyCollection._key + ), + { + instantiate: true, + storageKeys: [], + } + ); + }); + + it("should persist Group with formatted passed Key (config.followCollectionPersistKeyPattern)", () => { + group.persist("dummyKey", { followCollectionPersistKeyPattern: true }); + + expect(State.prototype.persist).toHaveBeenCalledWith( + CollectionPersistent.getGroupStorageKey( + "dummyKey", + dummyCollection._key + ), + { + instantiate: true, + storageKeys: [], + } + ); + }); + }); + + describe("rebuild function tests", () => { + beforeEach(() => { + group._value = ["dummyItem1Key", "dummyItem3Key", "dummyItem2Key"]; + }); + + it("should build Group output and items and set notFoundItemKeys to not found Item Keys", () => { + group.rebuild(); + + expect( + console.warn + ).toHaveBeenCalledWith( + `Agile Warn: Couldn't find some Items in Collection '${dummyCollection._key}' (${group._key})`, + ["dummyItem3Key"] + ); + expect(group.notFoundItemKeys).toStrictEqual(["dummyItem3Key"]); + expect(group.items).toStrictEqual([dummyItem1, dummyItem2]); + expect(group._output).toStrictEqual([ + dummyItem1._value, + dummyItem2._value, + ]); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/collection/item.test.ts b/packages/core/tests/unit/collection/item.test.ts new file mode 100644 index 00000000..cb51a48d --- /dev/null +++ b/packages/core/tests/unit/collection/item.test.ts @@ -0,0 +1,131 @@ +import { Item, Collection, Agile, StateObserver, State } from "../../../src"; + +describe("Item Tests", () => { + let dummyAgile: Agile; + let dummyCollection: Collection; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyCollection = new Collection(dummyAgile); + + jest.spyOn(State.prototype, "setKey"); + jest.spyOn(Item.prototype, "setKey"); + }); + + it("should create Item (default config)", () => { + // Overwrite setKey once to not call it + jest.spyOn(Item.prototype, "setKey").mockReturnValueOnce(undefined); + + const dummyData = { id: "dummyId", name: "dummyName" }; + const item = new Item(dummyCollection, dummyData); + + expect(item.collection()).toBe(dummyCollection); + expect(item.setKey).toHaveBeenCalledWith( + dummyData[dummyCollection.config.primaryKey] + ); + + expect(item._key).toBe(dummyData[dummyCollection.config.primaryKey]); + expect(item.valueType).toBeUndefined(); + expect(item.isSet).toBeFalsy(); + expect(item.isPlaceholder).toBeFalsy(); + expect(item.initialStateValue).toStrictEqual(dummyData); + expect(item._value).toStrictEqual(dummyData); + expect(item.previousStateValue).toStrictEqual(dummyData); + expect(item.nextStateValue).toStrictEqual(dummyData); + expect(item.observer).toBeInstanceOf(StateObserver); + expect(item.observer.deps.size).toBe(0); + expect(item.observer._key).toBe( + dummyData[dummyCollection.config.primaryKey] + ); + expect(item.sideEffects).toStrictEqual({}); + expect(item.computeMethod).toBeUndefined(); + expect(item.isPersisted).toBeFalsy(); + expect(item.persistent).toBeUndefined(); + expect(item.watchers).toStrictEqual({}); + expect(item.isSelected).toBeFalsy(); + }); + + it("should create Item (specific config)", () => { + // Overwrite setKey once to not call it + jest.spyOn(Item.prototype, "setKey").mockReturnValueOnce(undefined); + + const dummyData = { id: "dummyId", name: "dummyName" }; + const item = new Item(dummyCollection, dummyData, { + isPlaceholder: true, + }); + + expect(item.collection()).toBe(dummyCollection); + expect(item.setKey).toHaveBeenCalledWith( + dummyData[dummyCollection.config.primaryKey] + ); + + expect(item._key).toBe(dummyData[dummyCollection.config.primaryKey]); + expect(item.valueType).toBeUndefined(); + expect(item.isSet).toBeFalsy(); + expect(item.isPlaceholder).toBeTruthy(); + expect(item.initialStateValue).toStrictEqual(dummyData); + expect(item._value).toStrictEqual(dummyData); + expect(item.previousStateValue).toStrictEqual(dummyData); + expect(item.nextStateValue).toStrictEqual(dummyData); + expect(item.observer).toBeInstanceOf(StateObserver); + expect(item.observer.deps.size).toBe(0); + expect(item.observer._key).toBe( + dummyData[dummyCollection.config.primaryKey] + ); + expect(item.sideEffects).toStrictEqual({}); + expect(item.computeMethod).toBeUndefined(); + expect(item.isPersisted).toBeFalsy(); + expect(item.persistent).toBeUndefined(); + expect(item.watchers).toStrictEqual({}); + expect(item.isSelected).toBeFalsy(); + }); + + describe("Item Function Tests", () => { + let item: Item; + + beforeEach(() => { + item = new Item(dummyCollection, { id: "dummyId", name: "dummyName" }); + + item.removeSideEffect = jest.fn(); + jest.spyOn(item, "addSideEffect"); + dummyCollection.rebuildGroupsThatIncludeItemKey = jest.fn(); + }); + + describe("setKey function tests", () => { + it("should call State setKey and add rebuildGroupsThatIncludeItemKey sideEffect to it", () => { + item.setKey("myNewKey"); + + expect(State.prototype.setKey).toHaveBeenCalledWith("myNewKey"); + expect(item.removeSideEffect).toHaveBeenCalledWith( + Item.updateGroupSideEffectKey + ); + expect(item.addSideEffect).toHaveBeenCalledWith( + Item.updateGroupSideEffectKey, + expect.any(Function) + ); + + expect( + dummyCollection.rebuildGroupsThatIncludeItemKey + ).toHaveBeenCalledWith("myNewKey"); + }); + + describe("test added sideEffect called Item.updateGroupSideEffectKey", () => { + beforeEach(() => { + dummyCollection.rebuildGroupsThatIncludeItemKey = jest.fn(); + }); + + it("should call rebuildGroupThatIncludeItemKey", () => { + item.setKey("myNewKey"); + + item.sideEffects[Item.updateGroupSideEffectKey]({ + dummy: "property", + }); + + expect( + dummyCollection.rebuildGroupsThatIncludeItemKey + ).toHaveBeenCalledWith("myNewKey", { dummy: "property" }); + }); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/collection/selector.test.ts b/packages/core/tests/unit/collection/selector.test.ts new file mode 100644 index 00000000..948bc37b --- /dev/null +++ b/packages/core/tests/unit/collection/selector.test.ts @@ -0,0 +1,481 @@ +import { Selector, Agile, Collection, StateObserver, Item } from "../../../src"; + +describe("Selector Tests", () => { + interface ItemInterface { + id: string; + name: string; + } + + let dummyAgile: Agile; + let dummyCollection: Collection; + + beforeEach(() => { + jest.clearAllMocks(); + dummyAgile = new Agile({ localStorage: false }); + dummyCollection = new Collection(dummyAgile); + + jest.spyOn(Selector.prototype, "select"); + console.warn = jest.fn(); + }); + + it("should create Selector and call initial select (default config)", () => { + // Overwrite select once to not call it + jest.spyOn(Selector.prototype, "select").mockReturnValueOnce(undefined); + + const selector = new Selector(dummyCollection, "dummyItemKey"); + + expect(selector.collection()).toBe(dummyCollection); + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.select).toHaveBeenCalledWith("dummyItemKey", { + overwrite: true, + }); + + expect(selector._key).toBeUndefined(); + expect(selector.valueType).toBeUndefined(); + expect(selector.isSet).toBeFalsy(); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.initialStateValue).toBeUndefined(); + expect(selector._value).toBeUndefined(); + expect(selector.previousStateValue).toBeUndefined(); + expect(selector.nextStateValue).toBeUndefined(); + expect(selector.observer).toBeInstanceOf(StateObserver); + expect(selector.observer.deps.size).toBe(0); + expect(selector.observer._key).toBeUndefined(); + expect(selector.sideEffects).toStrictEqual({}); + expect(selector.computeMethod).toBeUndefined(); + expect(selector.isPersisted).toBeFalsy(); + expect(selector.persistent).toBeUndefined(); + expect(selector.watchers).toStrictEqual({}); + }); + + it("should create Selector and call initial select (specific config)", () => { + // Overwrite select once to not call it + jest.spyOn(Selector.prototype, "select").mockReturnValueOnce(undefined); + + const selector = new Selector(dummyCollection, "dummyItemKey", { + key: "dummyKey", + }); + + expect(selector.collection()).toBe(dummyCollection); + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.select).toHaveBeenCalledWith("dummyItemKey", { + overwrite: true, + }); + + expect(selector._key).toBe("dummyKey"); + expect(selector.valueType).toBeUndefined(); + expect(selector.isSet).toBeFalsy(); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.initialStateValue).toBeUndefined(); + expect(selector._value).toBeUndefined(); + expect(selector.previousStateValue).toBeUndefined(); + expect(selector.nextStateValue).toBeUndefined(); + expect(selector.observer).toBeInstanceOf(StateObserver); + expect(selector.observer.deps.size).toBe(0); + expect(selector.observer._key).toBe("dummyKey"); + expect(selector.sideEffects).toStrictEqual({}); + expect(selector.computeMethod).toBeUndefined(); + expect(selector.isPersisted).toBeFalsy(); + expect(selector.persistent).toBeUndefined(); + expect(selector.watchers).toStrictEqual({}); + }); + + it("should create Selector and shouldn't call initial select (config.isPlaceholder = true)", () => { + // Overwrite select once to not call it + jest.spyOn(Selector.prototype, "select").mockReturnValueOnce(undefined); + + const selector = new Selector(dummyCollection, "dummyItemKey", { + isPlaceholder: true, + }); + + expect(selector.collection()).toBe(dummyCollection); + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.select).not.toHaveBeenCalled(); + + expect(selector._key).toBeUndefined(); + expect(selector.valueType).toBeUndefined(); + expect(selector.isSet).toBeFalsy(); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.initialStateValue).toBeUndefined(); + expect(selector._value).toBeUndefined(); + expect(selector.previousStateValue).toBeUndefined(); + expect(selector.nextStateValue).toBeUndefined(); + expect(selector.observer).toBeInstanceOf(StateObserver); + expect(selector.observer.deps.size).toBe(0); + expect(selector.observer._key).toBeUndefined(); + expect(selector.sideEffects).toStrictEqual({}); + expect(selector.computeMethod).toBeUndefined(); + expect(selector.isPersisted).toBeFalsy(); + expect(selector.persistent).toBeUndefined(); + expect(selector.watchers).toStrictEqual({}); + }); + + describe("Selector Function Tests", () => { + let selector: Selector; + let dummyItem1: Item; + + beforeEach(() => { + dummyItem1 = new Item(dummyCollection, { + id: "dummyItem1", + name: "Frank", + }); + dummyCollection.data = { + dummyItem1: dummyItem1, + }; + + selector = new Selector(dummyCollection, "dummyItem1"); + }); + + describe("itemKey set function tests", () => { + it("should call select function with passed value", () => { + selector.select = jest.fn(); + + selector.itemKey = "newItemKey"; + + expect(selector.select).toHaveBeenCalledWith("newItemKey"); + }); + }); + + describe("itemKey get function tests", () => { + it("should return current ItemKey of Selector", () => { + selector._itemKey = "coolItemKey"; + + expect(selector.itemKey).toBe("coolItemKey"); + }); + }); + + describe("select function tests", () => { + let dummyItem2: Item; + + beforeEach(() => { + dummyItem2 = new Item(dummyCollection, { + id: "dummyItem2", + name: "Jeff", + }); + dummyCollection.data["dummyItem2"] = dummyItem2; + + selector.rebuildSelector = jest.fn(); + selector.unselect = jest.fn(); + dummyItem1.addSideEffect = jest.fn(); + dummyItem2.addSideEffect = jest.fn(); + }); + + it("should unselect old selected Item and select new Item (default config)", () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem2); + + selector.select("dummyItem2"); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem2" + ); + + expect(selector._itemKey).toBe("dummyItem2"); + expect(selector.item).toBe(dummyItem2); + expect(selector.unselect).toHaveBeenCalledWith({ background: true }); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + force: false, + overwrite: false, + storage: true, + }); + + expect(dummyItem2.addSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey, + expect.any(Function) + ); + expect(dummyItem2.isSelected).toBeTruthy(); + }); + + it("should unselect old selected Item and select new Item (specific config)", () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem2); + + selector.select("dummyItem2", { + force: true, + sideEffects: false, + background: true, + overwrite: true, + }); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem2" + ); + + expect(selector._itemKey).toBe("dummyItem2"); + expect(selector.item).toBe(dummyItem2); + expect(selector.unselect).toHaveBeenCalledWith({ background: true }); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: true, + sideEffects: false, + force: true, + overwrite: true, + storage: true, + }); + + expect(dummyItem2.addSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey, + expect.any(Function) + ); + expect(dummyItem2.isSelected).toBeTruthy(); + }); + + it("should print warning if trying to select selected Item again (default config)", () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem1); + + selector.select("dummyItem1"); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Selector has already selected 'dummyItem1'!" + ); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem1" + ); + expect(selector._itemKey).toBe("dummyItem1"); + expect(selector.item).toBe(dummyItem1); + expect(selector.unselect).not.toHaveBeenCalled(); + expect(selector.rebuildSelector).not.toHaveBeenCalled(); + + expect(dummyItem1.addSideEffect).not.toHaveBeenCalled(); + expect(dummyItem1.isSelected).toBeTruthy(); + }); + + it("should be able to select selected Item again (config.force = true)", () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem1); + + selector.select("dummyItem1", { force: true }); + + expect(console.warn).not.toHaveBeenCalled(); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem1" + ); + + expect(selector._itemKey).toBe("dummyItem1"); + expect(selector.item).toBe(dummyItem1); + expect(selector.unselect).toHaveBeenCalledWith({ background: true }); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + force: true, + overwrite: false, + storage: true, + }); + + expect(dummyItem1.addSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey, + expect.any(Function) + ); + expect(dummyItem1.isSelected).toBeTruthy(); + }); + + it("should remove old selected Item, select new Item and overwrite Selector if old Item is placeholder (default config)", async () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem2); + dummyItem1.isPlaceholder = true; + + selector.select("dummyItem2"); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem2" + ); + expect(selector._itemKey).toBe("dummyItem2"); + expect(selector.item).toBe(dummyItem2); + expect(selector.unselect).toHaveBeenCalledWith({ background: true }); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + force: false, + overwrite: true, + storage: true, + }); + + expect(dummyItem2.addSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey, + expect.any(Function) + ); + expect(dummyItem2.isSelected).toBeTruthy(); + }); + + it("should remove old selected Item, select new Item and shouldn't overwrite Selector if old Item is placeholder (config.overwrite = false)", async () => { + dummyCollection.getItemWithReference = jest.fn(() => dummyItem2); + dummyItem1.isPlaceholder = true; + + selector.select("dummyItem2", { overwrite: false }); + + expect(dummyCollection.getItemWithReference).toHaveBeenCalledWith( + "dummyItem2" + ); + expect(selector._itemKey).toBe("dummyItem2"); + expect(selector.item).toBe(dummyItem2); + expect(selector.unselect).toHaveBeenCalledWith({ background: true }); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + force: false, + overwrite: false, + storage: true, + }); + + expect(dummyItem2.addSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey, + expect.any(Function) + ); + expect(dummyItem2.isSelected).toBeTruthy(); + }); + + describe("test added sideEffect called Selector.rebuildSelectorSideEffectKey", () => { + beforeEach(() => { + selector.rebuildSelector = jest.fn(); + }); + + it("should call rebuildSelector", () => { + selector.select("dummyItem1"); + + dummyItem1.sideEffects[Selector.rebuildSelectorSideEffectKey]({ + dummy: "property", + }); + + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + dummy: "property", + }); + }); + }); + }); + + describe("unselect function tests", () => { + beforeEach(() => { + selector.rebuildSelector = jest.fn(); + dummyItem1.removeSideEffect = jest.fn(); + }); + + it("should unselect current selected Item and shouldn't remove it from Collection (default config)", () => { + selector.unselect(); + + expect(dummyItem1.isSelected).toBeFalsy(); + expect(dummyItem1.removeSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey + ); + + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.rebuildSelector).toHaveBeenCalledWith({}); + + expect(dummyCollection.data).toHaveProperty("dummyItem1"); + }); + + it("should unselect current selected Item and shouldn't remove it from Collection (specific config)", () => { + selector.unselect({ + background: true, + overwrite: true, + force: true, + }); + + expect(dummyItem1.isSelected).toBeFalsy(); + expect(dummyItem1.removeSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey + ); + + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.rebuildSelector).toHaveBeenCalledWith({ + background: true, + overwrite: true, + force: true, + }); + + expect(dummyCollection.data).toHaveProperty("dummyItem1"); + }); + + it("should unselect current selected placeholder Item and remove it from Collection (default config)", () => { + dummyItem1.isPlaceholder = true; + + selector.unselect(); + + expect(dummyItem1.isSelected).toBeFalsy(); + expect(dummyItem1.removeSideEffect).toHaveBeenCalledWith( + Selector.rebuildSelectorSideEffectKey + ); + + expect(selector.item).toBeUndefined(); + expect(selector._itemKey).toBe(Selector.dummyItemKey); + expect(selector.isPlaceholder).toBeTruthy(); + expect(selector.rebuildSelector).toHaveBeenCalledWith({}); + + expect(dummyCollection.data).not.toHaveProperty("dummyItem1"); + }); + }); + + describe("hasSelected function tests", () => { + beforeEach(() => { + selector._itemKey = "dummyItemKey"; + }); + + it("should return true if Selector has selected ItemKey and Item isSelected", () => { + selector.item.isSelected = true; + + expect(selector.hasSelected("dummyItemKey")).toBeTruthy(); + }); + + it("should return false if Selector hasn't selected ItemKey and Item isSelected", () => { + selector.item.isSelected = true; + + expect(selector.hasSelected("notSelectedItemKey")).toBeFalsy(); + }); + + it("should return false if Selector has selected ItemKey and Item isn't isSelected", () => { + selector.item.isSelected = false; + + expect(selector.hasSelected("dummyItemKey")).toBeFalsy(); + }); + + it("should return false if Selector hasn't selected ItemKey and Item isn't isSelected", () => { + selector.item.isSelected = false; + + expect(selector.hasSelected("notSelectedItemKey")).toBeFalsy(); + }); + }); + + describe("rebuildSelector function tests", () => { + beforeEach(() => { + selector.set = jest.fn(); + }); + + it("should set selector value to item value (default config)", () => { + selector.item = dummyItem1; + + selector.rebuildSelector(); + + expect(selector.set).toHaveBeenCalledWith(selector.item._value, {}); + }); + + it("should set selector value to item value (specific config)", () => { + selector.item = dummyItem1; + + selector.rebuildSelector({ + sideEffects: false, + background: true, + force: true, + }); + + expect(selector.set).toHaveBeenCalledWith(selector.item._value, { + sideEffects: false, + background: true, + force: true, + }); + }); + + it("should set selector value to undefined if Item is undefined (default config)", () => { + selector.item = undefined; + + selector.rebuildSelector(); + + expect(selector.set).toHaveBeenCalledWith(undefined, {}); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/computed/computed.test.ts b/packages/core/tests/unit/computed/computed.test.ts new file mode 100644 index 00000000..c1df8e96 --- /dev/null +++ b/packages/core/tests/unit/computed/computed.test.ts @@ -0,0 +1,300 @@ +import { + Computed, + Agile, + StateObserver, + Observer, + State, + ComputedTracker, +} from "../../../src"; + +describe("Computed Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + jest.spyOn(Computed.prototype, "recompute"); + console.error = jest.fn(); + }); + + it("should create Computed (default config)", () => { + const computedFunction = () => "computedValue"; + + const computed = new Computed(dummyAgile, computedFunction); + + expect(computed.computeFunction).toBe(computedFunction); + expect(computed.deps).toStrictEqual([]); + expect(computed.hardCodedDeps).toStrictEqual([]); + expect(computed._key).toBeUndefined(); + expect(computed.valueType).toBeUndefined(); + expect(computed.isSet).toBeFalsy(); + expect(computed.isPlaceholder).toBeFalsy(); + expect(computed.initialStateValue).toBe("computedValue"); + expect(computed._value).toBe("computedValue"); + expect(computed.previousStateValue).toBe("computedValue"); + expect(computed.nextStateValue).toBe("computedValue"); + expect(computed.observer).toBeInstanceOf(StateObserver); + expect(computed.observer.deps.size).toBe(0); + expect(computed.observer._key).toBeUndefined(); + expect(computed.sideEffects).toStrictEqual({}); + expect(computed.computeMethod).toBeUndefined(); + expect(computed.isPersisted).toBeFalsy(); + expect(computed.persistent).toBeUndefined(); + expect(computed.watchers).toStrictEqual({}); + + expect(computed.recompute).toHaveBeenCalled(); + }); + + it("should create Computed (specific config)", () => { + const dummyObserver1 = new Observer(dummyAgile); + const dummyObserver2 = new Observer(dummyAgile); + const dummyState = new State(dummyAgile, undefined); + const dummyStateObserver = new StateObserver(dummyState); + dummyState.observer = dummyStateObserver; + const computedFunction = () => "computedValue"; + + const computed = new Computed(dummyAgile, computedFunction, { + key: "coolComputed", + deps: [dummyObserver1], + computedDeps: [dummyObserver2, undefined, dummyState], + }); + + expect(computed.computeFunction).toBe(computedFunction); + expect(computed.deps).toStrictEqual([dummyObserver2, dummyStateObserver]); // x + expect(computed.hardCodedDeps).toStrictEqual([ + dummyObserver2, + dummyStateObserver, + ]); // x + expect(computed._key).toBe("coolComputed"); // x + expect(computed.valueType).toBeUndefined(); + expect(computed.isSet).toBeFalsy(); + expect(computed.isPlaceholder).toBeFalsy(); + expect(computed.initialStateValue).toBe("computedValue"); + expect(computed._value).toBe("computedValue"); + expect(computed.previousStateValue).toBe("computedValue"); + expect(computed.nextStateValue).toBe("computedValue"); + expect(computed.observer).toBeInstanceOf(StateObserver); + expect(computed.observer.deps.size).toBe(1); // x + expect(computed.observer.deps.has(dummyObserver1)).toBeTruthy(); // x + expect(computed.observer._key).toBe("coolComputed"); // x + expect(computed.sideEffects).toStrictEqual({}); + expect(computed.computeMethod).toBeUndefined(); + expect(computed.isPersisted).toBeFalsy(); + expect(computed.persistent).toBeUndefined(); + expect(computed.watchers).toStrictEqual({}); + + expect(computed.recompute).toHaveBeenCalled(); + }); + + describe("Computed Function Tests", () => { + let computed: Computed; + const dummyComputeFunction = jest.fn(() => "computedValue"); + + beforeEach(() => { + computed = new Computed(dummyAgile, dummyComputeFunction); + }); + + describe("recompute function tests", () => { + beforeEach(() => { + computed.ingest = jest.fn(); + }); + + it("should ingest Computed into Runtime (default config)", () => { + computed.recompute(); + + expect(computed.ingest).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + }); + }); + + it("should ingest Computed into Runtime (specific config)", () => { + computed.recompute({ + background: true, + sideEffects: false, + }); + + expect(computed.ingest).toHaveBeenCalledWith({ + background: true, + sideEffects: false, + }); + }); + }); + + describe("updateComputeFunction function tests", () => { + const newComputeFunction = () => "newComputedValue"; + let dummyObserver: Observer; + let oldDummyObserver: Observer; + let dummyStateObserver: StateObserver; + let dummyState: State; + + beforeEach(() => { + dummyObserver = new Observer(dummyAgile); + oldDummyObserver = new Observer(dummyAgile); + dummyState = new State(dummyAgile, undefined); + dummyStateObserver = new StateObserver(dummyState); + dummyState.observer = dummyStateObserver; + computed.hardCodedDeps = [oldDummyObserver]; + }); + + it("should updated computeFunction and overwrite hardCodedDeps (default config)", () => { + computed.updateComputeFunction(newComputeFunction, [ + dummyState, + dummyObserver, + ]); + + expect(computed.hardCodedDeps).toStrictEqual([ + dummyStateObserver, + dummyObserver, + ]); + expect(computed.deps).toStrictEqual([ + dummyStateObserver, + dummyObserver, + ]); + expect(computed.computeFunction).toBe(newComputeFunction); + expect(computed.recompute).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + }); + }); + + it("should updated computeFunction and overwrite hardCodedDeps (specific config)", () => { + computed.updateComputeFunction(newComputeFunction, [], { + background: true, + sideEffects: false, + }); + + expect(computed.hardCodedDeps).toStrictEqual([]); + expect(computed.deps).toStrictEqual([]); + expect(computed.computeFunction).toBe(newComputeFunction); + expect(computed.recompute).toHaveBeenCalledWith({ + background: true, + sideEffects: false, + }); + }); + + it("should updated computeFunction and add additional deps to hardCodedDeps (config.overwriteDeps = false)", () => { + computed.updateComputeFunction( + newComputeFunction, + [dummyState, dummyObserver], + { overwriteDeps: false } + ); + + expect(computed.hardCodedDeps).toStrictEqual([ + oldDummyObserver, + dummyStateObserver, + dummyObserver, + ]); + expect(computed.deps).toStrictEqual([ + oldDummyObserver, + dummyStateObserver, + dummyObserver, + ]); + expect(computed.computeFunction).toBe(newComputeFunction); + expect(computed.recompute).toHaveBeenCalledWith({ + background: false, + sideEffects: true, + }); + }); + }); + + describe("computeValue function tests", () => { + let dummyObserver1: Observer; + let dummyObserver2: Observer; + let dummyObserver3: Observer; + + beforeEach(() => { + dummyObserver1 = new Observer(dummyAgile); + dummyObserver2 = new Observer(dummyAgile); + dummyObserver3 = new Observer(dummyAgile); + + computed.hardCodedDeps = [dummyObserver3]; + + dummyObserver1.depend = jest.fn(); + dummyObserver2.depend = jest.fn(); + dummyObserver3.depend = jest.fn(); + ComputedTracker.track = jest.fn(); + }); + + it("should call computeFunction and track dependencies the computeFunction depends on", () => { + computed.computeFunction = jest.fn(() => "newComputedValue"); + ComputedTracker.getTrackedObservers = jest.fn(() => [ + dummyObserver1, + dummyObserver2, + ]); + + const response = computed.computeValue(); + + expect(response).toBe("newComputedValue"); + expect(dummyComputeFunction).toHaveBeenCalled(); + expect(ComputedTracker.track).toHaveBeenCalled(); + expect(ComputedTracker.getTrackedObservers).toHaveBeenCalled(); + expect(computed.hardCodedDeps).toStrictEqual([dummyObserver3]); + expect(computed.deps).toStrictEqual([ + dummyObserver3, + dummyObserver1, + dummyObserver2, + ]); + expect(dummyObserver1.depend).toHaveBeenCalledWith(computed.observer); + expect(dummyObserver2.depend).toHaveBeenCalledWith(computed.observer); + expect(dummyObserver3.depend).toHaveBeenCalledWith(computed.observer); + }); + }); + + describe("formatDeps function tests", () => { + let dummyObserver: Observer; + let dummyStateObserver: StateObserver; + let dummyState: State; + + beforeEach(() => { + dummyObserver = new Observer(dummyAgile); + dummyState = new State(dummyAgile, undefined); + dummyStateObserver = new StateObserver(dummyState); + + dummyState.observer = dummyStateObserver; + }); + + it("should return Observers that could be extracted from the passed Instances", () => { + const response = computed.formatDeps([ + dummyObserver, + dummyState, + undefined, + {}, + { observer: "fake" }, + ]); + + expect(response).toStrictEqual([dummyObserver, dummyStateObserver]); + }); + }); + + describe("patch function tests", () => { + it("should print error", () => { + computed.patch(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: You can't use patch method on ComputedState!" + ); + }); + }); + + describe("persist function tests", () => { + it("should print error", () => { + computed.persist(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: You can't use persist method on ComputedState!" + ); + }); + }); + + describe("invert function tests", () => { + it("should print error", () => { + computed.invert(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: You can't use invert method on ComputedState!" + ); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/computed/computed.tracker.test.ts b/packages/core/tests/unit/computed/computed.tracker.test.ts new file mode 100644 index 00000000..9848b29d --- /dev/null +++ b/packages/core/tests/unit/computed/computed.tracker.test.ts @@ -0,0 +1,74 @@ +import { ComputedTracker, Observer, Agile } from "../../../src"; + +describe("ComputedTracker Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + // Reset ComputedTracker (because it works static) + ComputedTracker.isTracking = false; + ComputedTracker.trackedObservers = new Set(); + }); + + describe("ComputedTracker Function Tests", () => { + describe("track function tests", () => { + it("should set isTracking to true", () => { + ComputedTracker.isTracking = false; + + ComputedTracker.track(); + + expect(ComputedTracker.isTracking).toBeTruthy(); + }); + }); + + describe("tracked function tests", () => { + let dummyObserver: Observer; + + beforeEach(() => { + dummyObserver = new Observer(dummyAgile); + }); + + it("should add passed Observer to trackedObservers if ComputedTracker is tracking", () => { + ComputedTracker.isTracking = true; + + ComputedTracker.tracked(dummyObserver); + + expect(ComputedTracker.trackedObservers.size).toBe(1); + expect( + ComputedTracker.trackedObservers.has(dummyObserver) + ).toBeTruthy(); + }); + + it("shouldn't add passed Observer to trackedObservers if ComputedTracker isn't tracking", () => { + ComputedTracker.isTracking = false; + + ComputedTracker.tracked(dummyObserver); + + expect(ComputedTracker.trackedObservers.size).toBe(0); + }); + }); + + describe("getTrackedObserver function tests", () => { + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyObserver1 = new Observer(dummyAgile); + dummyObserver2 = new Observer(dummyAgile); + + ComputedTracker.isTracking = true; + ComputedTracker.trackedObservers.add(dummyObserver1); + ComputedTracker.trackedObservers.add(dummyObserver2); + }); + + it("should return tracked Observers and reset tracking", () => { + const response = ComputedTracker.getTrackedObservers(); + + expect(response).toStrictEqual([dummyObserver1, dummyObserver2]); + expect(ComputedTracker.isTracking).toBeFalsy(); + expect(ComputedTracker.trackedObservers.size).toBe(0); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/event/event.job.test.ts b/packages/core/tests/unit/event/event.job.test.ts new file mode 100644 index 00000000..b6673df4 --- /dev/null +++ b/packages/core/tests/unit/event/event.job.test.ts @@ -0,0 +1,19 @@ +import { EventJob } from "../../../src"; + +describe("EventJob Tests", () => { + it("should create EventJob (without keys)", () => { + const eventJob = new EventJob("myPayload"); + + expect(eventJob.payload).toBe("myPayload"); + expect(eventJob.creationTimestamp).toBeCloseTo(new Date().getTime(), -1); + expect(eventJob.keys).toBeUndefined(); + }); + + it("should create EventJob (with keys)", () => { + const eventJob = new EventJob("myPayload", ["dummyKey1", "dummyKey2"]); + + expect(eventJob.payload).toBe("myPayload"); + expect(eventJob.creationTimestamp).toBeCloseTo(new Date().getTime(), -1); + expect(eventJob.keys).toStrictEqual(["dummyKey1", "dummyKey2"]); + }); +}); diff --git a/packages/core/tests/unit/event/event.observer.test.ts b/packages/core/tests/unit/event/event.observer.test.ts new file mode 100644 index 00000000..e77ec824 --- /dev/null +++ b/packages/core/tests/unit/event/event.observer.test.ts @@ -0,0 +1,116 @@ +import { + EventObserver, + Agile, + Observer, + SubscriptionContainer, + Event, + RuntimeJob, +} from "../../../src"; + +describe("EventObserver Tests", () => { + let dummyAgile: Agile; + let dummyEvent: Event; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyEvent = new Event(dummyAgile); + }); + + it("should create EventObserver (default config)", () => { + const eventObserver = new EventObserver(dummyEvent); + + expect(eventObserver).toBeInstanceOf(EventObserver); + expect(eventObserver.event()).toBe(dummyEvent); + expect(eventObserver.value).toBeUndefined(); + expect(eventObserver._key).toBeUndefined(); + expect(eventObserver.deps.size).toBe(0); + expect(eventObserver.subs.size).toBe(0); + }); + + it("should create EventObserver (specific config)", () => { + const dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + const dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + const dummySubscription1 = new SubscriptionContainer(); + const dummySubscription2 = new SubscriptionContainer(); + + const eventObserver = new EventObserver(dummyEvent, { + key: "testKey", + deps: [dummyObserver1, dummyObserver2], + subs: [dummySubscription1, dummySubscription2], + }); + + expect(eventObserver).toBeInstanceOf(EventObserver); + expect(eventObserver.event()).toBe(dummyEvent); + expect(eventObserver.value).toBeUndefined(); + expect(eventObserver._key).toBe("testKey"); + expect(eventObserver.deps.size).toBe(2); + expect(eventObserver.deps.has(dummyObserver2)).toBeTruthy(); + expect(eventObserver.deps.has(dummyObserver1)).toBeTruthy(); + expect(eventObserver.subs.size).toBe(2); + expect(eventObserver.subs.has(dummySubscription1)).toBeTruthy(); + expect(eventObserver.subs.has(dummySubscription2)).toBeTruthy(); + }); + + describe("EventObserver Function Tests", () => { + let eventObserver: EventObserver; + + beforeEach(() => { + eventObserver = new EventObserver(dummyEvent, { + key: "eventObserverKey", + }); + }); + + describe("trigger function tests", () => { + it("should create RuntimeJob and ingest it into the Runtime (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: RuntimeJob) => { + expect(job._key).toBe(eventObserver._key); + expect(job.observer).toBe(eventObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + }); + }); + + eventObserver.trigger(); + + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(RuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest Event into Runtime (specific config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: RuntimeJob) => { + expect(job._key).toBe("coolKey"); + expect(job.observer).toBe(eventObserver); + expect(job.config).toStrictEqual({ + background: true, + sideEffects: true, + force: true, + }); + }); + + eventObserver.trigger({ + background: true, + key: "coolKey", + perform: false, + force: true, + }); + + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(RuntimeJob), + { + perform: false, + } + ); + }); + }); + + describe("perform function tests", () => { + // No tests necessary + }); + }); +}); diff --git a/packages/core/tests/unit/event/event.test.ts b/packages/core/tests/unit/event/event.test.ts new file mode 100644 index 00000000..d62f9bca --- /dev/null +++ b/packages/core/tests/unit/event/event.test.ts @@ -0,0 +1,389 @@ +import { Event, Agile, Observer, EventObserver } from "../../../src"; +import * as Utils from "../../../src/utils"; + +describe("Event Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + console.error = jest.fn(); + }); + + it("should create Event (default config)", () => { + const event = new Event(dummyAgile); + + expect(event.config).toStrictEqual({ + maxUses: undefined, + delay: undefined, + overlap: false, + rerender: false, + }); + expect(event._key).toBeUndefined(); + expect(event.uses).toBe(0); + expect(event.callbacks).toStrictEqual({}); + expect(event.enabled).toBeTruthy(); + expect(event.observer).toBeInstanceOf(EventObserver); + expect(event.observer.deps.size).toBe(0); + expect(event.observer._key).toBeUndefined(); + expect(event.currentTimeout).toBeUndefined(); + expect(event.queue).toStrictEqual([]); + expect(event.payload).toBeUndefined(); + }); + + it("should create Event (specific config)", () => { + const dummyObserver = new Observer(dummyAgile); + + const event = new Event(dummyAgile, { + key: "coolEvent", + deps: [dummyObserver], + delay: 20, + maxUses: 40, + enabled: false, + rerender: true, + }); + + expect(event.config).toStrictEqual({ + maxUses: 40, + delay: 20, + overlap: false, + rerender: true, + }); + expect(event._key).toBe("coolEvent"); + expect(event.uses).toBe(0); + expect(event.callbacks).toStrictEqual({}); + expect(event.enabled).toBeFalsy(); + expect(event.observer).toBeInstanceOf(EventObserver); + expect(event.observer.deps.size).toBe(1); + expect(event.observer.deps.has(dummyObserver)).toBeTruthy(); + expect(event.observer._key).toBe("coolEvent"); + expect(event.currentTimeout).toBeUndefined(); + expect(event.queue).toStrictEqual([]); + expect(event.payload).toBeUndefined(); + }); + + describe("Event Function Tests", () => { + let event: Event; + + beforeEach(() => { + event = new Event(dummyAgile, { + key: "eventKey", + }); + }); + + describe("key set function tests", () => { + it("should call setKey with passed value", () => { + event.setKey = jest.fn(); + + event.key = "newKey"; + + expect(event.setKey).toHaveBeenCalledWith("newKey"); + }); + }); + + describe("key get function tests", () => { + it("should return current State Key", () => { + expect(event.key).toBe("eventKey"); + }); + }); + + describe("setKey function tests", () => { + it("should update existing Key in all instances", () => { + event.setKey("newKey"); + + expect(event.key).toBe("newKey"); + expect(event.observer._key).toBe("newKey"); + }); + }); + + describe("on function tests", () => { + const dummyCallbackFunction1 = () => {}; + const dummyCallbackFunction2 = () => {}; + + it("should add passed callbackFunction to callbacks at passed key", () => { + const response = event.on("dummyKey", dummyCallbackFunction1); + + expect(response).toBe(event); + expect(event.callbacks).toHaveProperty("dummyKey"); + expect(event.callbacks["dummyKey"]).toBe(dummyCallbackFunction1); + }); + + it("should add passed callbackFunction to callbacks at random key if no key passed and return that generated key", () => { + jest.spyOn(Utils, "generateId").mockReturnValue("randomKey"); + + const response = event.on(dummyCallbackFunction1); + + expect(response).toBe("randomKey"); + expect(event.callbacks).toHaveProperty("randomKey"); + expect(event.callbacks["randomKey"]).toBe(dummyCallbackFunction1); + expect(Utils.generateId).toHaveBeenCalled(); + }); + + it("shouldn't add passed invalid callbackFunction to callbacks at passed key", () => { + const response = event.on("dummyKey", "noFunction hehe" as any); + + expect(response).toBe(event); + expect(event.callbacks).not.toHaveProperty("dummyKey"); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: A Event Callback Function has to be typeof Function!" + ); + }); + + it("shouldn't add passed callbackFunction to callbacks at passed key if passed key is already occupied", () => { + event.callbacks["dummyKey"] = dummyCallbackFunction2; + + const response = event.on("dummyKey", dummyCallbackFunction1); + + expect(response).toBe(event); + expect(event.callbacks).toHaveProperty("dummyKey"); + expect(event.callbacks["dummyKey"]).toBe(dummyCallbackFunction2); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Event Callback Function with the key/name 'dummyKey' already exists!" + ); + }); + }); + + describe("trigger function tests", () => { + beforeEach(() => { + event.delayedTrigger = jest.fn(); + event.normalTrigger = jest.fn(); + }); + + it("should call normalTrigger if Event is enabled (config.delay = false)", () => { + event.enabled = true; + event.config.delay = undefined; + + event.trigger("myPayload", ["specificKey"]); + + expect(event.normalTrigger).toHaveBeenCalledWith("myPayload", [ + "specificKey", + ]); + expect(event.delayedTrigger).not.toHaveBeenCalled(); + }); + + it("shouldn't call normalTrigger if Event isn't enabled (config.delay = false)", () => { + event.enabled = false; + event.config.delay = undefined; + + event.trigger("myPayload", ["specificKey"]); + + expect(event.normalTrigger).not.toHaveBeenCalled(); + expect(event.delayedTrigger).not.toHaveBeenCalled(); + }); + + it("should call normalTrigger if Event is enabled (config.delay = false)", () => { + event.enabled = true; + event.config.delay = 10; + + event.trigger("myPayload", ["specificKey"]); + + expect(event.delayedTrigger).toHaveBeenCalledWith("myPayload", 10, [ + "specificKey", + ]); + expect(event.normalTrigger).not.toHaveBeenCalled(); + }); + + it("shouldn't call normalTrigger if Event isn't enabled (config.delay = false)", () => { + event.enabled = false; + event.config.delay = 10; + + event.trigger("myPayload", ["specificKey"]); + + expect(event.delayedTrigger).not.toHaveBeenCalled(); + expect(event.normalTrigger).not.toHaveBeenCalled(); + }); + }); + + describe("disable function tests", () => { + it("should disable Event", () => { + event.enabled = undefined; + + event.disable(); + + expect(event.enabled).toBeFalsy(); + }); + }); + + describe("enable function tests", () => { + it("should enable Event", () => { + event.enabled = undefined; + + event.enable(); + + expect(event.enabled).toBeTruthy(); + }); + }); + + describe("reset function tests", () => { + it("should reset enabled, uses and the currentTimeout", () => { + const timeout = setTimeout(() => {}, 1000); + // @ts-ignore + clearTimeout = jest.fn(); + event.enabled = undefined; + event.uses = 100; + event.currentTimeout = timeout; + + event.reset(); + + expect(event.enabled).toBeTruthy(); + expect(event.uses).toBe(0); + expect(event.currentTimeout).toBeUndefined(); + expect(clearTimeout).toHaveBeenCalledWith(timeout); + }); + }); + + describe("removeCallback function tests", () => { + beforeEach(() => { + event.callbacks["dummyKey"] = () => {}; + }); + + it("should remove callback at key from Event", () => { + event.removeCallback("dummyKey"); + + expect(event.callbacks).not.toHaveProperty("dummyKey"); + }); + }); + + describe("normalTrigger function tests", () => { + const dummyPayload = "123"; + const dummyCallbackFunction1 = jest.fn(); + const dummyCallbackFunction2 = jest.fn(); + const dummyCallbackFunction3 = jest.fn(); + + beforeEach(() => { + event.callbacks["callback1"] = dummyCallbackFunction1; + event.callbacks["callback2"] = dummyCallbackFunction2; + event.callbacks["callback3"] = dummyCallbackFunction3; + + event.observer.trigger = jest.fn(); + event.disable = jest.fn(); + }); + + it("should call callback functions at passed keys with passed payload", () => { + event.config.rerender = false; + event.uses = 0; + + event.normalTrigger(dummyPayload, ["callback1", "callback3"]); + + expect(dummyCallbackFunction1).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction2).not.toHaveBeenCalled(); + expect(dummyCallbackFunction3).toHaveBeenCalledWith(dummyPayload); + expect(event.observer.trigger).not.toHaveBeenCalled(); + expect(event.disable).not.toHaveBeenCalled(); + expect(event.uses).toBe(1); + }); + + it("should call all callback functions with passed payload", () => { + event.config.rerender = false; + event.uses = 0; + + event.normalTrigger(dummyPayload); + + expect(dummyCallbackFunction1).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction2).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction3).toHaveBeenCalledWith(dummyPayload); + expect(event.observer.trigger).not.toHaveBeenCalled(); + expect(event.disable).not.toHaveBeenCalled(); + expect(event.uses).toBe(1); + }); + + it("should call all callback functions and trigger a rerender (config.rerender)", () => { + event.config.rerender = true; + event.uses = 0; + + event.normalTrigger(dummyPayload); + + expect(dummyCallbackFunction1).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction2).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction3).toHaveBeenCalledWith(dummyPayload); + expect(event.observer.trigger).toHaveBeenCalled(); + expect(event.disable).not.toHaveBeenCalled(); + expect(event.uses).toBe(1); + }); + + it("should call all callback functions and disable event if maxUses got reached (config.maxUses)", () => { + event.config.maxUses = 2; + event.config.rerender = false; + event.uses = 0; + + event.normalTrigger(dummyPayload); + event.normalTrigger(dummyPayload); + + expect(dummyCallbackFunction1).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction2).toHaveBeenCalledWith(dummyPayload); + expect(dummyCallbackFunction3).toHaveBeenCalledWith(dummyPayload); + expect(event.observer.trigger).not.toHaveBeenCalled(); + expect(event.disable).toHaveBeenCalled(); + expect(event.uses).toBe(2); + }); + }); + + describe("delayedTrigger function tests", () => { + const dummyPayload1 = "123"; + const dummyPayload2 = "321"; + + beforeEach(() => { + event.normalTrigger = jest.fn(); + }); + + it("should execute one Event after the other", async () => { + event.config.overlap = false; + + event.delayedTrigger(dummyPayload1, 500, ["callback1", "callback3"]); + event.delayedTrigger(dummyPayload2, 500, ["callback2"]); + + expect(event.currentTimeout).not.toBeUndefined(); + expect(event.queue.length).toBe(1); + expect(event.queue[0].payload).toBe(dummyPayload2); + expect(event.queue[0].keys).toStrictEqual(["callback2"]); + expect(event.normalTrigger).not.toHaveBeenCalled(); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + // After executing first Event + expect(event.currentTimeout).not.toBeUndefined(); + expect(event.queue.length).toBe(0); + expect(event.normalTrigger).toHaveBeenCalledWith(dummyPayload1, [ + "callback1", + "callback3", + ]); + expect(event.normalTrigger).not.toHaveBeenCalledWith(dummyPayload2, [ + "callback2", + ]); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + // After executing second Event + expect(event.currentTimeout).toBeUndefined(); + expect(event.queue.length).toBe(0); + expect(event.normalTrigger).toHaveBeenCalledWith(dummyPayload2, [ + "callback2", + ]); + }); + + it("should execute Events at the same time (config.overlap = true)", async () => { + event.config.overlap = true; + + event.delayedTrigger(dummyPayload1, 500, ["callback1", "callback3"]); + event.delayedTrigger(dummyPayload2, 500, ["callback2"]); + + expect(event.currentTimeout).toBeUndefined(); + expect(event.queue.length).toBe(0); + expect(event.normalTrigger).not.toHaveBeenCalled(); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + // After executing both Events at the same time + expect(event.currentTimeout).toBeUndefined(); + expect(event.queue.length).toBe(0); + expect(event.normalTrigger).toHaveBeenCalledWith(dummyPayload1, [ + "callback1", + "callback3", + ]); + expect(event.normalTrigger).toHaveBeenCalledWith(dummyPayload2, [ + "callback2", + ]); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/integrations/integration.test.ts b/packages/core/tests/unit/integrations/integration.test.ts new file mode 100644 index 00000000..7ccab02c --- /dev/null +++ b/packages/core/tests/unit/integrations/integration.test.ts @@ -0,0 +1,47 @@ +import { Integration } from "../../../src"; + +describe("Integration Tests", () => { + it("should create Integration", () => { + const methods = { + bind: () => Promise.resolve(true), + updateMethod: () => {}, + }; + const integrationConfig = { + frameworkInstance: { react: "native" }, + key: "test", + ...methods, + }; + + const integration = new Integration(integrationConfig); + + expect(integration._key).toBe("test"); + expect(integration.frameworkInstance).toStrictEqual({ react: "native" }); + expect(integration.ready).toBeFalsy(); + expect(integration.integrated).toBeFalsy(); + expect(integration.methods).toStrictEqual(methods); + }); + + describe("Integration Function Tests", () => { + let integration: Integration; + + beforeEach(() => { + integration = new Integration({ key: "dummyIntegration" }); + }); + + describe("key set function tests", () => { + it("should update key in Integration", () => { + integration.key = "myCoolKey"; + + expect(integration._key).toBe("myCoolKey"); + }); + }); + + describe("key get function tests", () => { + it("should return current key of Integration", () => { + integration._key = "myCoolKey"; + + expect(integration.key).toBe("myCoolKey"); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/integrations/integrations.test.ts b/packages/core/tests/unit/integrations/integrations.test.ts new file mode 100644 index 00000000..89bac99d --- /dev/null +++ b/packages/core/tests/unit/integrations/integrations.test.ts @@ -0,0 +1,147 @@ +import { Agile, Integration, Integrations } from "../../../src"; + +describe("Integrations Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + Agile.initialIntegrations = []; + + console.error = jest.fn(); + console.warn = jest.fn(); + jest.spyOn(Integrations.prototype, "integrate"); + }); + + it("should create Integrations", () => { + const integrations = new Integrations(dummyAgile); + + expect(integrations.integrations.size).toBe(0); + }); + + it("should create Integrations and integrate Agile initialIntegrations", async () => { + const dummyIntegration1 = new Integration({ + key: "initialIntegration1", + }); + const dummyIntegration2 = new Integration({ + key: "initialIntegration2", + }); + Agile.initialIntegrations.push(dummyIntegration1); + Agile.initialIntegrations.push(dummyIntegration2); + + const integrations = new Integrations(dummyAgile); + + expect(integrations.integrations.size).toBe(2); + expect(integrations.integrations.has(dummyIntegration1)).toBeTruthy(); + expect(integrations.integrations.has(dummyIntegration2)).toBeTruthy(); + + expect(integrations.integrate).toHaveBeenCalledWith(dummyIntegration1); + expect(integrations.integrate).toHaveBeenCalledWith(dummyIntegration2); + }); + + describe("Integrations Function Tests", () => { + let integrations: Integrations; + let dummyIntegration1: Integration; + let dummyIntegration2: Integration; + + beforeEach(() => { + integrations = new Integrations(dummyAgile); + dummyIntegration1 = new Integration({ + key: "dummyIntegration1", + }); + dummyIntegration2 = new Integration({ + key: "dummyIntegration2", + }); + }); + + describe("integrate function tests", () => { + it("should integrate valid integration with no bind function", async () => { + const response = await integrations.integrate(dummyIntegration1); + + expect(response).toBeTruthy(); + expect(integrations.integrations.has(dummyIntegration1)).toBeTruthy(); + expect(dummyIntegration1.ready).toBeTruthy(); + expect(dummyIntegration1.integrated).toBeTruthy(); + }); + + it("should integrate valid integration with bind function that returns true", async () => { + dummyIntegration1.methods.bind = jest.fn(() => Promise.resolve(true)); + + const response = await integrations.integrate(dummyIntegration1); + + expect(response).toBeTruthy(); + expect(integrations.integrations.has(dummyIntegration1)).toBeTruthy(); + expect(dummyIntegration1.ready).toBeTruthy(); + expect(dummyIntegration1.integrated).toBeTruthy(); + expect(dummyIntegration1.methods.bind).toHaveBeenCalledWith(dummyAgile); + }); + + it("should integrate valid integration with bind function that returns false", async () => { + dummyIntegration1.methods.bind = jest.fn(() => Promise.resolve(false)); + + const response = await integrations.integrate(dummyIntegration1); + + expect(response).toBeTruthy(); + expect(integrations.integrations.has(dummyIntegration1)).toBeTruthy(); + expect(dummyIntegration1.ready).toBeFalsy(); + expect(dummyIntegration1.integrated).toBeTruthy(); + expect(dummyIntegration1.methods.bind).toHaveBeenCalledWith(dummyAgile); + }); + + it("shouldn't integrate Integration that has no valid Key", async () => { + dummyIntegration1._key = undefined; + + const response = await integrations.integrate(dummyIntegration1); + + expect(response).toBeFalsy(); + expect(integrations.integrations.has(dummyIntegration1)).toBeFalsy(); + expect(dummyIntegration1.ready).toBeFalsy(); + expect(dummyIntegration1.integrated).toBeFalsy(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Failed to integrate framework! Invalid Integration!", + dummyIntegration1._key + ); + }); + }); + + describe("update function tests", () => { + const dummyComponentInstance = { my: "component" }; + const dummyUpdatedData = { my: "updatedData" }; + + beforeEach(() => { + integrations.integrate(dummyIntegration1); + integrations.integrate(dummyIntegration2); + }); + + it("should call updateMethod on each ready Integration", () => { + dummyIntegration1.ready = false; + dummyIntegration1.methods.updateMethod = jest.fn(); + dummyIntegration2.methods.updateMethod = jest.fn(); + + integrations.update(dummyComponentInstance, dummyUpdatedData); + + expect(dummyIntegration1.methods.updateMethod).not.toHaveBeenCalled(); + expect(dummyIntegration2.methods.updateMethod).toHaveBeenCalledWith( + dummyComponentInstance, + dummyUpdatedData + ); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Integration 'dummyIntegration1' isn't ready yet!" + ); + }); + }); + + describe("hasIntegration function tests", () => { + it("should return false if Integrations has no integrated Integration", () => { + expect(integrations.hasIntegration()).toBeFalsy(); + }); + + it("should return true if Integrations has at least one integrated Integration", () => { + integrations.integrate(dummyIntegration1); + + expect(integrations.hasIntegration()).toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/runtime/observer.test.ts b/packages/core/tests/unit/runtime/observer.test.ts new file mode 100644 index 00000000..b5d44c7f --- /dev/null +++ b/packages/core/tests/unit/runtime/observer.test.ts @@ -0,0 +1,169 @@ +import { + Observer, + Agile, + SubscriptionContainer, + RuntimeJob, +} from "../../../src"; + +describe("Observer Tests", () => { + let dummyAgile: Agile; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + let dummySubscription1: SubscriptionContainer; + let dummySubscription2: SubscriptionContainer; + + beforeEach(() => { + dummyAgile = new Agile(); + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + dummySubscription1 = new SubscriptionContainer(); + dummySubscription2 = new SubscriptionContainer(); + + console.warn = jest.fn(); + jest.spyOn(Observer.prototype, "subscribe"); + }); + + it("should create Observer (default config)", () => { + const observer = new Observer(dummyAgile); + + expect(observer._key).toBeUndefined(); + expect(observer.value).toBeUndefined(); + expect(observer.deps.size).toBe(0); + expect(observer.subs.size).toBe(0); + }); + + it("should create Observer (specific config)", () => { + const observer = new Observer(dummyAgile, { + key: "testKey", + subs: [dummySubscription1, dummySubscription2], + deps: [dummyObserver1, dummyObserver2], + value: "coolValue", + }); + + expect(observer._key).toBe("testKey"); + expect(observer.value).toBe("coolValue"); + expect(observer.deps.size).toBe(2); + expect(observer.deps.has(dummyObserver2)).toBeTruthy(); + expect(observer.deps.has(dummyObserver1)).toBeTruthy(); + expect(observer.subs.size).toBe(2); + expect(observer.subs.has(dummySubscription1)).toBeTruthy(); + expect(observer.subs.has(dummySubscription2)).toBeTruthy(); + + expect(observer.subscribe).toHaveBeenCalledWith(dummySubscription1); + expect(observer.subscribe).toHaveBeenCalledWith(dummySubscription2); + }); + + describe("Observer Function Tests", () => { + let observer: Observer; + + beforeEach(() => { + observer = new Observer(dummyAgile, { key: "observer" }); + }); + + describe("key set function tests", () => { + it("should update key in Observer", () => { + observer.key = "myNewDummyKey"; + + expect(observer._key).toBe("myNewDummyKey"); + }); + }); + + describe("key get function tests", () => { + it("should return current key of Observer", () => { + observer._key = "myDummyKey"; + + expect(observer.key).toBe("myDummyKey"); + }); + }); + + describe("perform function tests", () => { + it("should print warning", () => { + const dummyJob = new RuntimeJob(observer); + + observer.perform(dummyJob); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Perform function isn't Set in Observer! Be aware that Observer is no stand alone class!" + ); + }); + }); + + describe("depend function tests", () => { + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + }); + + it("should add passed Observer to deps", () => { + observer.depend(dummyObserver1); + + expect(observer.deps.size).toBe(1); + expect(observer.deps.has(dummyObserver2)); + }); + + it("shouldn't add the same Observer twice to deps", () => { + observer.depend(dummyObserver1); + + observer.depend(dummyObserver1); + + expect(observer.deps.size).toBe(1); + expect(observer.deps.has(dummyObserver1)); + }); + }); + + describe("subscribe function tests", () => { + let dummySubscriptionContainer1: SubscriptionContainer; + let dummySubscriptionContainer2: SubscriptionContainer; + + beforeEach(() => { + dummySubscriptionContainer1 = new SubscriptionContainer(); + dummySubscriptionContainer2 = new SubscriptionContainer(); + }); + + it("should add subscriptionContainer to subs and this(Observer) to SubscriptionContainer subs", () => { + observer.subscribe(dummySubscriptionContainer1); + + expect(observer.subs.size).toBe(1); + expect(observer.subs.has(dummySubscriptionContainer1)); + expect(dummySubscriptionContainer1.subs.size).toBe(1); + expect(dummySubscriptionContainer1.subs.has(observer)).toBeTruthy(); + }); + + it("shouldn't add same subscriptionContainer twice to subs", () => { + observer.subscribe(dummySubscriptionContainer1); + + observer.subscribe(dummySubscriptionContainer1); + + expect(observer.subs.size).toBe(1); + expect(observer.subs.has(dummySubscriptionContainer1)); + expect(dummySubscriptionContainer1.subs.size).toBe(1); + expect(dummySubscriptionContainer1.subs.has(observer)).toBeTruthy(); + }); + }); + + describe("unsubscribe function tests", () => { + let dummySubscriptionContainer1: SubscriptionContainer; + let dummySubscriptionContainer2: SubscriptionContainer; + + beforeEach(() => { + dummySubscriptionContainer1 = new SubscriptionContainer(); + dummySubscriptionContainer2 = new SubscriptionContainer(); + observer.subscribe(dummySubscriptionContainer1); + observer.subscribe(dummySubscriptionContainer2); + }); + + it("should remove subscriptionContainer from subs and this(Observer) from SubscriptionContainer subs", () => { + observer.unsubscribe(dummySubscriptionContainer1); + + expect(observer.subs.size).toBe(1); + expect(observer.subs.has(dummySubscriptionContainer1)); + expect(dummySubscriptionContainer1.subs.size).toBe(0); + expect(dummySubscriptionContainer2.subs.size).toBe(1); + expect(dummySubscriptionContainer2.subs.has(observer)).toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/runtime/runtime.job.test.ts b/packages/core/tests/unit/runtime/runtime.job.test.ts new file mode 100644 index 00000000..77d0f4ef --- /dev/null +++ b/packages/core/tests/unit/runtime/runtime.job.test.ts @@ -0,0 +1,109 @@ +import { Agile, Integration, RuntimeJob, Observer } from "../../../src"; + +describe("RuntimeJob Tests", () => { + let dummyAgile: Agile; + let dummyIntegration: Integration; + let dummyObserver: Observer; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyIntegration = new Integration({ + key: "myIntegration", + }); + dummyObserver = new Observer(dummyAgile); + }); + + it("should create RuntimeJob with Agile that has integrations (default config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new RuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has integrations (specific config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new RuntimeJob(dummyObserver, { + key: "dummyJob", + sideEffects: false, + force: true, + }); + + expect(job._key).toBe("dummyJob"); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: false, + force: true, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has no integrations (default config)", () => { + const job = new RuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob and Agile that has integrations (config.background = true)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new RuntimeJob(dummyObserver, { background: true }); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: true, + sideEffects: true, + force: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + describe("RuntimeJob Function Tests", () => { + let job: RuntimeJob; + + beforeEach(() => { + job = new RuntimeJob(dummyObserver); + }); + + describe("key get function tests", () => { + it("should return key of RuntimeJob", () => { + job._key = "myCoolKey"; + + expect(job.key).toBe("myCoolKey"); + }); + }); + + describe("key set function tests", () => { + it("should update key in RuntimeJob", () => { + job.key = "myCoolKey"; + + expect(job._key).toBe("myCoolKey"); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/runtime/runtime.test.ts b/packages/core/tests/unit/runtime/runtime.test.ts new file mode 100644 index 00000000..16fcedce --- /dev/null +++ b/packages/core/tests/unit/runtime/runtime.test.ts @@ -0,0 +1,439 @@ +import { + Agile, + CallbackSubscriptionContainer, + ComponentSubscriptionContainer, + RuntimeJob, + Observer, + Runtime, + SubscriptionContainer, +} from "../../../src"; +import testIntegration from "../../helper/test.integration"; + +describe("Runtime Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + console.warn = jest.fn(); + }); + + it("should create Runtime", () => { + const runtime = new Runtime(dummyAgile); + + expect(runtime.currentJob).toBeNull(); + expect(runtime.jobQueue).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(0); + expect(runtime.jobsToRerender).toStrictEqual([]); + }); + + describe("Runtime Function Tests", () => { + let runtime: Runtime; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + let dummyObserver3: Observer; + + beforeEach(() => { + runtime = new Runtime(dummyAgile); + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + dummyObserver3 = new Observer(dummyAgile, { key: "dummyObserver3" }); + }); + + describe("ingest function tests", () => { + let dummyJob: RuntimeJob; + + beforeEach(() => { + dummyJob = new RuntimeJob(dummyObserver1); + + runtime.perform = jest.fn(); + }); + + it("should perform passed Job (default config)", () => { + runtime.ingest(dummyJob); + + expect(runtime.jobQueue.length).toBe(0); + expect(runtime.perform).toHaveBeenCalledWith(dummyJob); + }); + + it("shouldn't perform passed Job (config.perform = false)", () => { + runtime.ingest(dummyJob, { perform: false }); + + expect(runtime.jobQueue.length).toBe(1); + expect(runtime.jobQueue[0]).toBe(dummyJob); + expect(runtime.perform).not.toHaveBeenCalled(); + }); + }); + + describe("perform function tests", () => { + let dummyJob1: RuntimeJob; + let dummyJob2: RuntimeJob; + let dummyJob3: RuntimeJob; + + beforeEach(() => { + dummyJob1 = new RuntimeJob(dummyObserver1, { key: "dummyJob1" }); + dummyJob2 = new RuntimeJob(dummyObserver2, { key: "dummyJob2" }); + dummyJob3 = new RuntimeJob(dummyObserver1, { key: "dummyJob3" }); + dummyJob1.rerender = true; + dummyJob2.rerender = true; + dummyJob3.rerender = false; + + runtime.updateSubscribers = jest.fn(); + jest.spyOn(dummyObserver1, "perform"); + jest.spyOn(dummyObserver2, "perform"); + }); + + it("should perform passed and all in jobQueue remaining Jobs and call updateSubscribers", async () => { + runtime.jobQueue.push(dummyJob2); + runtime.jobQueue.push(dummyJob3); + + runtime.perform(dummyJob1); + + expect(dummyObserver1.perform).toHaveBeenCalledWith(dummyJob1); + expect(dummyJob1.performed).toBeTruthy(); + expect(dummyObserver2.perform).toHaveBeenCalledWith(dummyJob2); + expect(dummyJob2.performed).toBeTruthy(); + expect(dummyObserver1.perform).toHaveBeenCalledWith(dummyJob3); + expect(dummyJob3.performed).toBeTruthy(); + + expect(runtime.jobQueue.length).toBe(0); + expect(runtime.jobsToRerender.length).toBe(2); + expect(runtime.jobsToRerender.includes(dummyJob1)).toBeTruthy(); + expect(runtime.jobsToRerender.includes(dummyJob2)).toBeTruthy(); + expect(runtime.jobsToRerender.includes(dummyJob3)).toBeFalsy(); + + // Sleep 5ms because updateSubscribers get called in Timeout + await new Promise((resolve) => setTimeout(resolve, 5)); + + expect(runtime.updateSubscribers).toHaveBeenCalledTimes(1); + }); + + it("should perform passed Job and all in jobQueue remaining and shouldn't call updateSubscribes if no job needs to rerender", async () => { + dummyJob1.rerender = false; + runtime.jobQueue.push(dummyJob3); + + runtime.perform(dummyJob1); + + expect(dummyObserver1.perform).toHaveBeenCalledWith(dummyJob1); + expect(dummyJob1.performed).toBeTruthy(); + expect(dummyObserver1.perform).toHaveBeenCalledWith(dummyJob3); + expect(dummyJob3.performed).toBeTruthy(); + + expect(runtime.jobQueue.length).toBe(0); + expect(runtime.jobsToRerender.length).toBe(0); + + // Sleep 5ms because updateSubscribers get called in Timeout + await new Promise((resolve) => setTimeout(resolve, 5)); + + expect(runtime.updateSubscribers).not.toHaveBeenCalled(); + }); + }); + + describe("updateSubscribers function tests", () => { + let dummyObserver4: Observer; + let rCallbackSubJob: RuntimeJob; + let nrArCallbackSubJob: RuntimeJob; + let rComponentSubJob: RuntimeJob; + let nrArComponentSubJob: RuntimeJob; + let rCallbackSubContainer: CallbackSubscriptionContainer; + let rCallbackSubContainerCallbackFunction = () => {}; + let nrCallbackSubContainer: CallbackSubscriptionContainer; + let nrCallbackSubContainerCallbackFunction = () => {}; + let rComponentSubContainer: ComponentSubscriptionContainer; + let rComponentSubContainerComponent = { + my: "cool component", + }; + let nrComponentSubContainer: ComponentSubscriptionContainer; + let nrComponentSubContainerComponent = { + my: "second cool component", + }; + + beforeEach(() => { + dummyAgile.integrate(testIntegration); + dummyObserver4 = new Observer(dummyAgile, { key: "dummyObserver4" }); + + dummyObserver1.value = "dummyObserverValue1"; + dummyObserver2.value = "dummyObserverValue2"; + dummyObserver3.value = "dummyObserverValue3"; + dummyObserver4.value = "dummyObserverValue4"; + + // Create Ready Callback Subscription + rCallbackSubContainer = dummyAgile.subController.subscribeWithSubsArray( + rCallbackSubContainerCallbackFunction, + [dummyObserver1, dummyObserver2] + ) as CallbackSubscriptionContainer; + rCallbackSubContainer.callback = jest.fn(); + + // Create Not Ready Callback Subscription + nrCallbackSubContainer = dummyAgile.subController.subscribeWithSubsArray( + nrCallbackSubContainerCallbackFunction, + [dummyObserver2] + ) as CallbackSubscriptionContainer; + nrCallbackSubContainer.callback = jest.fn(); + nrCallbackSubContainer.ready = false; + + // Create Ready Component Subscription + rComponentSubContainer = dummyAgile.subController.subscribeWithSubsObject( + rComponentSubContainerComponent, + { + observer3: dummyObserver3, + observer4: dummyObserver4, + } + ).subscriptionContainer as ComponentSubscriptionContainer; + + // Create Not Ready Component Subscription + nrComponentSubContainer = dummyAgile.subController.subscribeWithSubsObject( + nrComponentSubContainerComponent, + { + observer4: dummyObserver4, + } + ).subscriptionContainer as ComponentSubscriptionContainer; + nrComponentSubContainer.ready = false; + + rComponentSubJob = new RuntimeJob(dummyObserver3, { key: "dummyJob3" }); // Job with ready Component Subscription + rCallbackSubJob = new RuntimeJob(dummyObserver1, { key: "dummyJob1" }); // Job with ready CallbackSubscription + nrArComponentSubJob = new RuntimeJob(dummyObserver4, { + key: "dummyJob4", + }); // Job with not ready and ready Component Subscription + nrArCallbackSubJob = new RuntimeJob(dummyObserver2, { + key: "dummyJob2", + }); // Job with not ready and ready Callback Subscription + + jest.spyOn(dummyAgile.integrations, "update"); + jest.spyOn(runtime, "handleObjectBasedSubscription"); + }); + + it("should return false if agile has no integration", () => { + dummyAgile.hasIntegration = jest.fn(() => false); + runtime.jobsToRerender.push(rCallbackSubJob); + runtime.jobsToRerender.push(nrArCallbackSubJob); + + const response = runtime.updateSubscribers(); + + expect(response).toBeFalsy(); + expect(runtime.jobsToRerender).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(0); + expect(dummyAgile.integrations.update).not.toHaveBeenCalled(); + expect(rCallbackSubContainer.callback).not.toHaveBeenCalled(); + expect(nrCallbackSubContainer.callback).not.toHaveBeenCalled(); + }); + + it("should return false if no Jobs in jobsToRerender and notReadyJobsToRerender left", () => { + dummyAgile.hasIntegration = jest.fn(() => true); + runtime.jobsToRerender = []; + runtime.notReadyJobsToRerender = new Set(); + + const response = runtime.updateSubscribers(); + + expect(response).toBeFalsy(); + }); + + it("should update ready component based Subscription", () => { + dummyAgile.hasIntegration = jest.fn(() => true); + runtime.jobsToRerender.push(rComponentSubJob); + + runtime.updateSubscribers(); + + expect(runtime.jobsToRerender).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(0); + + expect(dummyAgile.integrations.update).toHaveBeenCalledTimes(1); + expect(dummyAgile.integrations.update).toHaveBeenCalledWith( + rComponentSubContainerComponent, + { + observer3: "dummyObserverValue3", + } + ); + expect(runtime.handleObjectBasedSubscription).toHaveBeenCalledWith( + rComponentSubContainer, + rComponentSubJob + ); + expect(rComponentSubJob.subscriptionContainersToUpdate.size).toBe(0); + expect(dummyObserver3.subs.size).toBe(1); + }); + + it("should update ready callback based Subscription", () => { + dummyAgile.hasIntegration = jest.fn(() => true); + runtime.jobsToRerender.push(rCallbackSubJob); + + runtime.updateSubscribers(); + + expect(runtime.jobsToRerender).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(0); + + expect(rCallbackSubContainer.callback).toHaveBeenCalledTimes(1); + expect(rCallbackSubJob.subscriptionContainersToUpdate.size).toBe(0); + expect(dummyObserver1.subs.size).toBe(1); + }); + + it("shouldn't update not ready Subscriptions", () => { + dummyAgile.hasIntegration = jest.fn(() => true); + runtime.jobsToRerender.push(nrArCallbackSubJob); + runtime.jobsToRerender.push(nrArComponentSubJob); + + runtime.updateSubscribers(); + + expect(runtime.jobsToRerender).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(2); + expect( + runtime.notReadyJobsToRerender.has(nrArCallbackSubJob) + ).toBeTruthy(); + expect( + runtime.notReadyJobsToRerender.has(nrArComponentSubJob) + ).toBeTruthy(); + + expect(nrArCallbackSubJob.subscriptionContainersToUpdate.size).toBe(1); + expect( + nrArCallbackSubJob.subscriptionContainersToUpdate.has( + nrCallbackSubContainer + ) + ).toBeTruthy(); + expect(nrArComponentSubJob.subscriptionContainersToUpdate.size).toBe(1); + expect( + nrArComponentSubJob.subscriptionContainersToUpdate.has( + nrComponentSubContainer + ) + ).toBeTruthy(); + + expect(rCallbackSubContainer.callback).toHaveBeenCalledTimes(1); + expect(nrCallbackSubContainer.callback).not.toHaveBeenCalled(); + + expect(dummyAgile.integrations.update).toHaveBeenCalledTimes(1); + expect(dummyAgile.integrations.update).toHaveBeenCalledWith( + rComponentSubContainerComponent, + { + observer4: "dummyObserverValue4", + } + ); + expect(dummyAgile.integrations.update).not.toHaveBeenCalledWith( + nrComponentSubContainerComponent, + { + observer4: "dummyObserverValue4", + } + ); + + expect(dummyObserver2.subs.size).toBe(2); + expect(dummyObserver4.subs.size).toBe(2); + + expect(runtime.handleObjectBasedSubscription).toHaveBeenCalledWith( + rComponentSubContainer, + nrArComponentSubJob + ); + expect(runtime.handleObjectBasedSubscription).not.toHaveBeenCalledWith( + nrComponentSubContainer, + nrArComponentSubJob + ); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: SubscriptionContainer/Component isn't ready to rerender!", + nrCallbackSubContainer + ); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: SubscriptionContainer/Component isn't ready to rerender!", + nrComponentSubContainer + ); + }); + + it("should update in the past not ready Subscriptions in notReadyJobsToUpdate", () => { + dummyAgile.hasIntegration = jest.fn(() => true); + runtime.notReadyJobsToRerender.add(rCallbackSubJob); + + runtime.updateSubscribers(); + + expect(runtime.jobsToRerender).toStrictEqual([]); + expect(runtime.notReadyJobsToRerender.size).toBe(0); + + expect(rCallbackSubContainer.callback).toHaveBeenCalled(); + expect(rCallbackSubJob.subscriptionContainersToUpdate.size).toBe(0); + expect(dummyObserver1.subs.size).toBe(1); + }); + }); + + describe("handleObjectBasedSubscription function tests", () => { + let arraySubscriptionContainer: SubscriptionContainer; + let dummyComponent = { + my: "cool component", + }; + let objectSubscriptionContainer: SubscriptionContainer; + let dummyComponent2 = { + my: "second cool component", + }; + let arrayJob: RuntimeJob; + let objectJob1: RuntimeJob; + let objectJob2: RuntimeJob; + + beforeEach(() => { + arraySubscriptionContainer = dummyAgile.subController.subscribeWithSubsArray( + dummyComponent, + [dummyObserver1, dummyObserver2, dummyObserver3] + ); + arrayJob = new RuntimeJob(dummyObserver1, { key: "dummyArrayJob" }); + + objectSubscriptionContainer = dummyAgile.subController.subscribeWithSubsObject( + dummyComponent2, + { + observer1: dummyObserver1, + observer2: dummyObserver2, + observer3: dummyObserver3, + } + ).subscriptionContainer; + objectJob1 = new RuntimeJob(dummyObserver1, { key: "dummyObjectJob1" }); + objectJob2 = new RuntimeJob(dummyObserver3, { key: "dummyObjectJob2" }); + }); + + it("should ignore not object based SubscriptionContainer", () => { + runtime.handleObjectBasedSubscription( + arraySubscriptionContainer, + arrayJob + ); + + expect(arraySubscriptionContainer.observerKeysToUpdate).toStrictEqual( + [] + ); + }); + + it("should add Job Observer to changedObjectKeys in SubscriptionContainer", () => { + runtime.handleObjectBasedSubscription( + objectSubscriptionContainer, + objectJob1 + ); + + expect(objectSubscriptionContainer.observerKeysToUpdate).toStrictEqual([ + "observer1", + ]); + }); + }); + + describe("getObjectBasedProps function tests", () => { + let subscriptionContainer: SubscriptionContainer; + let dummyFunction = () => {}; + + beforeEach(() => { + subscriptionContainer = dummyAgile.subController.subscribeWithSubsObject( + dummyFunction, + { + observer1: dummyObserver1, + observer2: dummyObserver2, + observer3: dummyObserver3, + } + ).subscriptionContainer; + dummyObserver1.value = "dummyObserverValue1"; + dummyObserver3.value = "dummyObserverValue3"; + }); + + it("should build Observer Value Object out of observerKeysToUpdate and Value of Observer", () => { + subscriptionContainer.observerKeysToUpdate.push("observer1"); + subscriptionContainer.observerKeysToUpdate.push("observer2"); + subscriptionContainer.observerKeysToUpdate.push("observer3"); + + const props = runtime.getObjectBasedProps(subscriptionContainer); + + expect(props).toStrictEqual({ + observer1: "dummyObserverValue1", + observer3: "dummyObserverValue3", + }); + expect(subscriptionContainer.observerKeysToUpdate).toStrictEqual([]); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/runtime/subscription/container/CallbackSubscriptionContainer.test.ts b/packages/core/tests/unit/runtime/subscription/container/CallbackSubscriptionContainer.test.ts new file mode 100644 index 00000000..a8eb040a --- /dev/null +++ b/packages/core/tests/unit/runtime/subscription/container/CallbackSubscriptionContainer.test.ts @@ -0,0 +1,38 @@ +import { + Agile, + CallbackSubscriptionContainer, + Observer, +} from "../../../../../src"; + +describe("CallbackSubscriptionContainer Tests", () => { + let dummyAgile: Agile; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyAgile = new Agile(); + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + }); + + it("should create CallbackSubscriptionContainer", () => { + const dummyIntegration = () => {}; + + const subscriptionContainer = new CallbackSubscriptionContainer( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "dummyKey" + ); + + expect(subscriptionContainer.callback).toBe(dummyIntegration); + + expect(subscriptionContainer.key).toBe("dummyKey"); + expect(subscriptionContainer.ready).toBeFalsy(); + expect(subscriptionContainer.subs.size).toBe(2); + expect(subscriptionContainer.subs.has(dummyObserver1)).toBeTruthy(); + expect(subscriptionContainer.subs.has(dummyObserver2)).toBeTruthy(); + expect(subscriptionContainer.isObjectBased).toBeFalsy(); + expect(subscriptionContainer.observerKeysToUpdate).toStrictEqual([]); + expect(subscriptionContainer.subsObject).toBeUndefined(); + }); +}); diff --git a/packages/core/tests/unit/runtime/subscription/container/ComponentSubscriptionContainer.test.ts b/packages/core/tests/unit/runtime/subscription/container/ComponentSubscriptionContainer.test.ts new file mode 100644 index 00000000..e3d3f5e1 --- /dev/null +++ b/packages/core/tests/unit/runtime/subscription/container/ComponentSubscriptionContainer.test.ts @@ -0,0 +1,38 @@ +import { + Agile, + ComponentSubscriptionContainer, + Observer, +} from "../../../../../src"; + +describe("ComponentSubscriptionContainer Tests", () => { + let dummyAgile: Agile; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyAgile = new Agile(); + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + }); + + it("should create ComponentSubscriptionContainer", () => { + const dummyIntegration = { dummy: "integration" }; + + const subscriptionContainer = new ComponentSubscriptionContainer( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "dummyKey" + ); + + expect(subscriptionContainer.component).toStrictEqual(dummyIntegration); + + expect(subscriptionContainer.key).toBe("dummyKey"); + expect(subscriptionContainer.ready).toBeFalsy(); + expect(subscriptionContainer.subs.size).toBe(2); + expect(subscriptionContainer.subs.has(dummyObserver1)).toBeTruthy(); + expect(subscriptionContainer.subs.has(dummyObserver2)).toBeTruthy(); + expect(subscriptionContainer.isObjectBased).toBeFalsy(); + expect(subscriptionContainer.observerKeysToUpdate).toStrictEqual([]); + expect(subscriptionContainer.subsObject).toBeUndefined(); + }); +}); diff --git a/packages/core/tests/unit/runtime/subscription/container/SubscriptionContainer.test.ts b/packages/core/tests/unit/runtime/subscription/container/SubscriptionContainer.test.ts new file mode 100644 index 00000000..c46b4360 --- /dev/null +++ b/packages/core/tests/unit/runtime/subscription/container/SubscriptionContainer.test.ts @@ -0,0 +1,29 @@ +import { Agile, Observer, SubscriptionContainer } from "../../../../../src"; + +describe("SubscriptionContainer Tests", () => { + let dummyAgile: Agile; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyAgile = new Agile(); + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + }); + + it("should create SubscriptionContainer", () => { + const subscriptionContainer = new SubscriptionContainer( + [dummyObserver1, dummyObserver2], + "dummyKey" + ); + + expect(subscriptionContainer.key).toBe("dummyKey"); + expect(subscriptionContainer.ready).toBeFalsy(); + expect(subscriptionContainer.subs.size).toBe(2); + expect(subscriptionContainer.subs.has(dummyObserver1)).toBeTruthy(); + expect(subscriptionContainer.subs.has(dummyObserver2)).toBeTruthy(); + expect(subscriptionContainer.isObjectBased).toBeFalsy(); + expect(subscriptionContainer.observerKeysToUpdate).toStrictEqual([]); + expect(subscriptionContainer.subsObject).toBeUndefined(); + }); +}); diff --git a/packages/core/tests/unit/runtime/subscription/sub.controller.test.ts b/packages/core/tests/unit/runtime/subscription/sub.controller.test.ts new file mode 100644 index 00000000..768b4480 --- /dev/null +++ b/packages/core/tests/unit/runtime/subscription/sub.controller.test.ts @@ -0,0 +1,430 @@ +import { + Agile, + CallbackSubscriptionContainer, + ComponentSubscriptionContainer, + Observer, + SubController, + SubscriptionContainer, +} from "../../../../src"; + +describe("SubController Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + }); + + it("should create SubController", () => { + const subController = new SubController(dummyAgile); + + expect(subController.callbackSubs.size).toBe(0); + expect(subController.callbackSubs.size).toBe(0); + }); + + describe("SubController Function Tests", () => { + let subController: SubController; + let dummyObserver1: Observer; + let dummyObserver2: Observer; + + beforeEach(() => { + dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + subController = new SubController(dummyAgile); + }); + + describe("subscribeWithSubsObject function tests", () => { + const dummyIntegration = "myDummyIntegration"; + let dummySubscriptionContainer: SubscriptionContainer; + + beforeEach(() => { + dummySubscriptionContainer = new SubscriptionContainer(); + dummyObserver1.value = "myCoolValue"; + + subController.registerSubscription = jest.fn( + () => dummySubscriptionContainer + ); + jest.spyOn(dummyObserver1, "subscribe"); + jest.spyOn(dummyObserver2, "subscribe"); + }); + + it("should create subscriptionContainer and add in Object shape passed Observers to it", () => { + const subscribeWithSubsResponse = subController.subscribeWithSubsObject( + dummyIntegration, + { + dummyObserver1: dummyObserver1, + dummyObserver2: dummyObserver2, + }, + "subscribeWithSubsObjectKey" + ); + + expect(subscribeWithSubsResponse).toStrictEqual({ + props: { + dummyObserver1: "myCoolValue", + }, + subscriptionContainer: dummySubscriptionContainer, + }); + + expect(subController.registerSubscription).toHaveBeenCalledWith( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "subscribeWithSubsObjectKey" + ); + + expect(dummySubscriptionContainer.isObjectBased).toBeTruthy(); + expect(dummySubscriptionContainer.subsObject).toStrictEqual({ + dummyObserver1: dummyObserver1, + dummyObserver2: dummyObserver2, + }); + + expect(dummySubscriptionContainer.subs.size).toBe(2); + expect( + dummySubscriptionContainer.subs.has(dummyObserver1) + ).toBeTruthy(); + expect( + dummySubscriptionContainer.subs.has(dummyObserver2) + ).toBeTruthy(); + + expect(dummyObserver1.subscribe).toHaveBeenCalledWith( + dummySubscriptionContainer + ); + expect(dummyObserver2.subscribe).toHaveBeenCalledWith( + dummySubscriptionContainer + ); + }); + }); + + describe("subscribeWithSubsArray function tests", () => { + const dummyIntegration = "myDummyIntegration"; + let dummySubscriptionContainer: SubscriptionContainer; + + beforeEach(() => { + dummySubscriptionContainer = new SubscriptionContainer(); + + subController.registerSubscription = jest.fn( + () => dummySubscriptionContainer + ); + jest.spyOn(dummyObserver1, "subscribe"); + jest.spyOn(dummyObserver2, "subscribe"); + }); + + it("should create subscriptionContainer and add in Array Shape passed Observers to it", () => { + const subscribeWithSubsArrayResponse = subController.subscribeWithSubsArray( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "subscribeWithSubsArrayKey" + ); + + expect(subscribeWithSubsArrayResponse).toBe(dummySubscriptionContainer); + + expect(subController.registerSubscription).toHaveBeenCalledWith( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "subscribeWithSubsArrayKey" + ); + + expect(dummySubscriptionContainer.isObjectBased).toBeFalsy(); + expect(dummySubscriptionContainer.subsObject).toBeUndefined(); + + expect(dummySubscriptionContainer.subs.size).toBe(2); + expect( + dummySubscriptionContainer.subs.has(dummyObserver1) + ).toBeTruthy(); + expect( + dummySubscriptionContainer.subs.has(dummyObserver2) + ).toBeTruthy(); + + expect(dummyObserver1.subscribe).toHaveBeenCalledWith( + dummySubscriptionContainer + ); + expect(dummyObserver2.subscribe).toHaveBeenCalledWith( + dummySubscriptionContainer + ); + }); + }); + + describe("unsubscribe function tests", () => { + beforeEach(() => { + jest.spyOn(dummyObserver1, "unsubscribe"); + jest.spyOn(dummyObserver2, "unsubscribe"); + }); + + it("should unsubscribe callbackSubscriptionContainer", () => { + const dummyIntegration = () => {}; + const callbackSubscriptionContainer = subController.registerCallbackSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + subController.unsubscribe(callbackSubscriptionContainer); + + expect(subController.callbackSubs.size).toBe(0); + expect(callbackSubscriptionContainer.ready).toBeFalsy(); + expect(dummyObserver1.unsubscribe).toHaveBeenCalledWith( + callbackSubscriptionContainer + ); + expect(dummyObserver2.unsubscribe).toHaveBeenCalledWith( + callbackSubscriptionContainer + ); + }); + + it("should unsubscribe componentSubscriptionContainer", () => { + const dummyIntegration: any = { + dummy: "integration", + }; + const componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + subController.unsubscribe(componentSubscriptionContainer); + + expect(subController.componentSubs.size).toBe(0); + expect(componentSubscriptionContainer.ready).toBeFalsy(); + expect(dummyObserver1.unsubscribe).toHaveBeenCalledWith( + componentSubscriptionContainer + ); + expect(dummyObserver2.unsubscribe).toHaveBeenCalledWith( + componentSubscriptionContainer + ); + }); + + it("should unsubscribe componentSubscriptionContainer in a passed Object that hold an instance of it", () => { + const dummyIntegration: any = { + dummy: "integration", + }; + const componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + subController.unsubscribe(dummyIntegration); + + expect(subController.componentSubs.size).toBe(0); + expect(componentSubscriptionContainer.ready).toBeFalsy(); + expect(dummyObserver1.unsubscribe).toHaveBeenCalledWith( + componentSubscriptionContainer + ); + expect(dummyObserver2.unsubscribe).toHaveBeenCalledWith( + componentSubscriptionContainer + ); + }); + }); + + describe("registerSubscription function tests", () => { + let dummySubscriptionContainer: SubscriptionContainer; + + beforeEach(() => { + dummySubscriptionContainer = new SubscriptionContainer(); + + subController.registerCallbackSubscription = jest.fn( + () => dummySubscriptionContainer as CallbackSubscriptionContainer + ); + subController.registerComponentSubscription = jest.fn( + () => dummySubscriptionContainer as ComponentSubscriptionContainer + ); + }); + + it("should call registerCallbackSubscription if passed integrationInstance is a Function", () => { + const dummyIntegration = () => {}; + + const subscriptionContainer = subController.registerSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "niceKey" + ); + + expect(subscriptionContainer).toBe(dummySubscriptionContainer); + expect(subController.registerCallbackSubscription).toHaveBeenCalledWith( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "niceKey" + ); + expect( + subController.registerComponentSubscription + ).not.toHaveBeenCalled(); + }); + + it("should call registerComponentSubscription if passed integrationInstance is not a Function", () => { + const dummyIntegration = { dummy: "integration" }; + + const subscriptionContainer = subController.registerSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "niceKey" + ); + + expect(subscriptionContainer).toBe(dummySubscriptionContainer); + expect( + subController.registerComponentSubscription + ).toHaveBeenCalledWith( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "niceKey" + ); + expect( + subController.registerCallbackSubscription + ).not.toHaveBeenCalled(); + }); + }); + + describe("registerComponentSubscription function tests", () => { + it("should return ready componentSubscriptionContainer and add it to dummyIntegration (agileInstance.config.mount = false)", () => { + const dummyIntegration: any = { dummy: "integration" }; + + const componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + expect(componentSubscriptionContainer).toBeInstanceOf( + ComponentSubscriptionContainer + ); + expect(componentSubscriptionContainer.key).toBe("myKey"); + expect(componentSubscriptionContainer.component).toStrictEqual( + dummyIntegration + ); + expect(componentSubscriptionContainer.ready).toBeTruthy(); + + expect(componentSubscriptionContainer.subs.size).toBe(2); + expect( + componentSubscriptionContainer.subs.has(dummyObserver1) + ).toBeTruthy(); + expect( + componentSubscriptionContainer.subs.has(dummyObserver2) + ).toBeTruthy(); + + expect(subController.componentSubs.size).toBe(1); + expect( + subController.componentSubs.has(componentSubscriptionContainer) + ).toBeTruthy(); + + expect(dummyIntegration.componentSubscriptionContainer).toBe( + componentSubscriptionContainer + ); + }); + + it("should return not ready componentSubscriptionContainer if componentInstance isn't mounted (agileInstance.config.mount = true)", () => { + dummyAgile.config.waitForMount = true; + const dummyIntegration: any = { + dummy: "integration", + }; + + const componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + expect(componentSubscriptionContainer).toBeInstanceOf( + ComponentSubscriptionContainer + ); + expect(componentSubscriptionContainer.ready).toBeFalsy(); + }); + + it("should return ready componentSubscriptionContainer if componentInstance is mounted (agileInstance.config.mount = true)", () => { + dummyAgile.config.waitForMount = true; + const dummyIntegration: any = { + dummy: "integration", + }; + subController.mount(dummyIntegration); + + const componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + expect(componentSubscriptionContainer).toBeInstanceOf( + ComponentSubscriptionContainer + ); + expect(componentSubscriptionContainer.ready).toBeTruthy(); + }); + }); + + describe("registerCallbackSubscription function tests", () => { + it("should return callbackSubscriptionContainer", () => { + const dummyIntegration = () => {}; + + const callbackSubscriptionContainer = subController.registerCallbackSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + + expect(callbackSubscriptionContainer).toBeInstanceOf( + CallbackSubscriptionContainer + ); + expect(callbackSubscriptionContainer.key).toBe("myKey"); + expect(callbackSubscriptionContainer.callback).toBe(dummyIntegration); + expect(callbackSubscriptionContainer.ready).toBeTruthy(); + + expect(callbackSubscriptionContainer.subs.size).toBe(2); + expect( + callbackSubscriptionContainer.subs.has(dummyObserver1) + ).toBeTruthy(); + expect( + callbackSubscriptionContainer.subs.has(dummyObserver2) + ).toBeTruthy(); + + expect(subController.callbackSubs.size).toBe(1); + expect( + subController.callbackSubs.has(callbackSubscriptionContainer) + ).toBeTruthy(); + }); + }); + + describe("mount function tests", () => { + const dummyIntegration: any = { + dummy: "integration", + }; + let componentSubscriptionContainer: ComponentSubscriptionContainer; + + beforeEach(() => { + dummyAgile.config.waitForMount = true; + componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + }); + + it("should add componentInstance to mountedComponents and set its subscriptionContainer to ready", () => { + subController.mount(dummyIntegration); + + expect(componentSubscriptionContainer.ready).toBeTruthy(); + expect(subController.mountedComponents.size).toBe(1); + expect( + subController.mountedComponents.has(dummyIntegration) + ).toBeTruthy(); + }); + }); + + describe("unmount function tests", () => { + const dummyIntegration: any = { + dummy: "integration", + }; + let componentSubscriptionContainer: ComponentSubscriptionContainer; + + beforeEach(() => { + dummyAgile.config.waitForMount = true; + componentSubscriptionContainer = subController.registerComponentSubscription( + dummyIntegration, + [dummyObserver1, dummyObserver2], + "myKey" + ); + subController.mount(dummyIntegration); + }); + + it("should remove componentInstance from mountedComponents and set its subscriptionContainer to not ready", () => { + subController.unmount(dummyIntegration); + + expect(componentSubscriptionContainer.ready).toBeFalsy(); + expect(subController.mountedComponents.size).toBe(0); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/state/state.observer.test.ts b/packages/core/tests/unit/state/state.observer.test.ts new file mode 100644 index 00000000..a8cff5d9 --- /dev/null +++ b/packages/core/tests/unit/state/state.observer.test.ts @@ -0,0 +1,375 @@ +import { + Agile, + Computed, + StateRuntimeJob, + Observer, + State, + StateObserver, + StatePersistent, + SubscriptionContainer, +} from "../../../src"; + +describe("StateObserver Tests", () => { + let dummyAgile: Agile; + let dummyState: State; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyState = new State(dummyAgile, "dummyValue", { key: "dummyState" }); + }); + + it("should create StateObserver (default config)", () => { + const stateObserver = new StateObserver(dummyState); + + expect(stateObserver).toBeInstanceOf(StateObserver); + expect(stateObserver.nextStateValue).toBe("dummyValue"); + expect(stateObserver.state()).toBe(dummyState); + expect(stateObserver.value).toBe("dummyValue"); + expect(stateObserver._key).toBeUndefined(); + expect(stateObserver.deps.size).toBe(0); + expect(stateObserver.subs.size).toBe(0); + }); + + it("should create StateObserver (specific config)", () => { + const dummyObserver1 = new Observer(dummyAgile, { key: "dummyObserver1" }); + const dummyObserver2 = new Observer(dummyAgile, { key: "dummyObserver2" }); + const dummySubscription1 = new SubscriptionContainer(); + const dummySubscription2 = new SubscriptionContainer(); + + const stateObserver = new StateObserver(dummyState, { + key: "testKey", + deps: [dummyObserver1, dummyObserver2], + subs: [dummySubscription1, dummySubscription2], + }); + + expect(stateObserver).toBeInstanceOf(StateObserver); + expect(stateObserver.nextStateValue).toBe("dummyValue"); + expect(stateObserver.state()).toBe(dummyState); + expect(stateObserver.value).toBe("dummyValue"); + expect(stateObserver._key).toBe("testKey"); + expect(stateObserver.deps.size).toBe(2); + expect(stateObserver.deps.has(dummyObserver2)).toBeTruthy(); + expect(stateObserver.deps.has(dummyObserver1)).toBeTruthy(); + expect(stateObserver.subs.size).toBe(2); + expect(stateObserver.subs.has(dummySubscription1)).toBeTruthy(); + expect(stateObserver.subs.has(dummySubscription2)).toBeTruthy(); + }); + + describe("StateObserver Function Tests", () => { + let stateObserver: StateObserver; + + beforeEach(() => { + stateObserver = new StateObserver(dummyState, { + key: "stateObserverKey", + }); + }); + + describe("ingest function tests", () => { + let computedObserver: StateObserver; + let dummyComputed: Computed; + + beforeEach(() => { + dummyComputed = new Computed(dummyAgile, () => {}, { + key: "dummyComputed", + }); + computedObserver = new StateObserver(dummyComputed, { + key: "computedObserverKey", + }); + + stateObserver.ingestValue = jest.fn(); + computedObserver.ingestValue = jest.fn(); + }); + + it("should call ingestValue with nextStateValue (default config)", () => { + dummyState.nextStateValue = "nextValue"; + + stateObserver.ingest(); + + expect(stateObserver.ingestValue).toHaveBeenCalledWith("nextValue", {}); + }); + + it("should call ingestValue with nextStateValue (specific config)", () => { + dummyState.nextStateValue = "nextValue"; + + stateObserver.ingest({ + force: true, + key: "coolKey", + storage: false, + sideEffects: false, + background: true, + perform: false, + }); + + expect(stateObserver.ingestValue).toHaveBeenCalledWith("nextValue", { + force: true, + key: "coolKey", + storage: false, + sideEffects: false, + background: true, + perform: false, + }); + }); + + it("should call ingestValue with computedValue if Observer belongs to a ComputedState (default config)", () => { + dummyComputed.computeValue = jest.fn(() => "computedValue"); + + computedObserver.ingest(); + + expect(computedObserver.ingestValue).toHaveBeenCalledWith( + "computedValue", + {} + ); + expect(dummyComputed.computeValue).toHaveBeenCalled(); + }); + }); + + describe("ingestValue function tests", () => { + beforeEach(() => { + dummyAgile.runtime.ingest = jest.fn(); + }); + + it("should ingest State into Runtime if newValue isn't equal to currentValue (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + }); + + stateObserver.ingestValue("updatedDummyValue"); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest State into Runtime if newValue isn't equal to currentValue (specific config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe("dummyJob"); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: false, + force: true, + storage: true, + overwrite: true, + }); + }); + + stateObserver.ingestValue("updatedDummyValue", { + perform: false, + force: true, + sideEffects: false, + overwrite: true, + key: "dummyJob", + }); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: false, + } + ); + }); + + it("shouldn't ingest State into Runtime if newValue is equal to currentValue (default config)", () => { + dummyState._value = "updatedDummyValue"; + + stateObserver.ingestValue("updatedDummyValue"); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).not.toHaveBeenCalled(); + }); + + it("should ingest State into Runtime if newValue is equal to currentValue (config.force = true)", () => { + dummyState._value = "updatedDummyValue"; + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: true, + storage: true, + overwrite: false, + }); + }); + + stateObserver.ingestValue("updatedDummyValue", { force: true }); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest placeholder State into Runtime (default config)", () => { + dummyAgile.runtime.ingest = jest.fn((job: StateRuntimeJob) => { + expect(job._key).toBe(stateObserver._key); + expect(job.observer).toBe(stateObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: true, + storage: true, + overwrite: true, + }); + }); + dummyState.isPlaceholder = true; + + stateObserver.ingestValue("updatedDummyValue"); + + expect(stateObserver.nextStateValue).toBe("updatedDummyValue"); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + + it("should ingest State into Runtime and compute newStateValue if State compute Function is set (default config)", () => { + dummyState.computeMethod = (value) => `cool value '${value}'`; + + stateObserver.ingestValue("updatedDummyValue"); + + expect(stateObserver.nextStateValue).toBe( + "cool value 'updatedDummyValue'" + ); + expect(dummyAgile.runtime.ingest).toHaveBeenCalledWith( + expect.any(StateRuntimeJob), + { + perform: true, + } + ); + }); + }); + + describe("perform function tests", () => { + let dummyJob: StateRuntimeJob; + + beforeEach(() => { + dummyJob = new StateRuntimeJob(stateObserver, { + key: "dummyJob", + }); + dummyState.persistent = new StatePersistent(dummyState); + dummyState.isPersisted = true; + + stateObserver.sideEffects = jest.fn(); + }); + + it("should perform Job", () => { + dummyJob.observer.nextStateValue = "newValue"; + dummyState.initialStateValue = "initialValue"; + dummyState._value = "dummyValue"; + + stateObserver.perform(dummyJob); + + expect(dummyState.previousStateValue).toBe("dummyValue"); + expect(dummyState.initialStateValue).toBe("initialValue"); + expect(dummyState._value).toBe("newValue"); + expect(dummyState.nextStateValue).toBe("newValue"); + expect(dummyState.isSet).toBeTruthy(); + expect(stateObserver.value).toBe("newValue"); + expect(stateObserver.sideEffects).toHaveBeenCalledWith(dummyJob); + }); + + it("should perform Job and overwrite State (job.config.overwrite = true)", () => { + dummyJob.observer.nextStateValue = "newValue"; + dummyJob.config.overwrite = true; + dummyState.isPlaceholder = true; + dummyState.initialStateValue = "overwriteValue"; + dummyState._value = "dummyValue"; + + stateObserver.perform(dummyJob); + + expect(dummyState.previousStateValue).toBe("newValue"); + expect(dummyState.initialStateValue).toBe("newValue"); + expect(dummyState._value).toBe("newValue"); + expect(dummyState.nextStateValue).toBe("newValue"); + expect(dummyState.isSet).toBeFalsy(); + expect(dummyState.isPlaceholder).toBeFalsy(); + expect(stateObserver.value).toBe("newValue"); + expect(stateObserver.sideEffects).toHaveBeenCalledWith(dummyJob); + }); + + it("should perform Job and set isSet to false if initialStateValue equals to newStateValue", () => { + dummyJob.observer.nextStateValue = "newValue"; + dummyState.initialStateValue = "newValue"; + dummyState._value = "dummyValue"; + + stateObserver.perform(dummyJob); + + expect(dummyState.previousStateValue).toBe("dummyValue"); + expect(dummyState.initialStateValue).toBe("newValue"); + expect(dummyState._value).toBe("newValue"); + expect(dummyState.nextStateValue).toBe("newValue"); + expect(dummyState.isSet).toBeFalsy(); + expect(stateObserver.value).toBe("newValue"); + expect(stateObserver.sideEffects).toHaveBeenCalledWith(dummyJob); + }); + }); + + describe("sideEffects function tests", () => { + let dummyJob: StateRuntimeJob; + let dummyStateObserver: StateObserver; + + beforeEach(() => { + dummyStateObserver = new StateObserver(new State(dummyAgile, "test")); + dummyJob = new StateRuntimeJob(stateObserver, { + key: "dummyJob", + }); + + dummyState.observer.deps.add(dummyStateObserver); + + dummyState.watchers["dummyWatcher"] = jest.fn(); + dummyState.sideEffects["dummySideEffect"] = jest.fn(); + dummyStateObserver.ingest = jest.fn(); + }); + + it("should call watchers, sideEffects and ingest dependencies of State", () => { + dummyState._value = "dummyValue"; + stateObserver.sideEffects(dummyJob); + + expect(dummyState.watchers["dummyWatcher"]).toHaveBeenCalledWith( + "dummyValue" + ); + expect(dummyState.sideEffects["dummySideEffect"]).toHaveBeenCalledWith( + dummyJob.config + ); + expect(dummyStateObserver.ingest).toHaveBeenCalledWith({ + perform: false, + }); + }); + + it("should call watchers, ingest dependencies of State and shouldn't call sideEffects (job.config.sideEffects = false)", () => { + dummyState._value = "dummyValue"; + dummyJob.config.sideEffects = false; + stateObserver.sideEffects(dummyJob); + + expect(dummyState.watchers["dummyWatcher"]).toHaveBeenCalledWith( + "dummyValue" + ); + expect( + dummyState.sideEffects["dummySideEffect"] + ).not.toHaveBeenCalled(); + expect(dummyStateObserver.ingest).toHaveBeenCalledWith({ + perform: false, + }); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/state/state.persistent.test.ts b/packages/core/tests/unit/state/state.persistent.test.ts new file mode 100644 index 00000000..31e60f9f --- /dev/null +++ b/packages/core/tests/unit/state/state.persistent.test.ts @@ -0,0 +1,471 @@ +import { + Agile, + State, + StatePersistent, + Storage, + Persistent, +} from "../../../src"; + +describe("StatePersistent Tests", () => { + let dummyAgile: Agile; + let dummyState: State; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyAgile = new Agile({ localStorage: false }); + dummyState = new State(dummyAgile, "dummyValue"); + + jest.spyOn(StatePersistent.prototype, "instantiatePersistent"); + jest.spyOn(StatePersistent.prototype, "initialLoading"); + console.error = jest.fn(); + }); + + it("should create StatePersistent and shouldn't call initialLoading if Persistent isn't ready (default config)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(StatePersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = false; + }); + + const statePersistent = new StatePersistent(dummyState); + + expect(statePersistent).toBeInstanceOf(StatePersistent); + expect(statePersistent.state()).toBe(dummyState); + expect(statePersistent.instantiatePersistent).toHaveBeenCalledWith({ + key: undefined, + storageKeys: [], + }); + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + + expect(statePersistent._key).toBe(StatePersistent.placeHolderKey); + expect(statePersistent.ready).toBeFalsy(); + expect(statePersistent.isPersisted).toBeFalsy(); + expect(statePersistent.onLoad).toBeUndefined(); + expect(statePersistent.storageKeys).toStrictEqual([]); + expect(statePersistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create StatePersistent and shouldn't call initialLoading if Persistent isn't ready (specific config)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(StatePersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = false; + }); + + const statePersistent = new StatePersistent(dummyState, { + key: "statePersistentKey", + storageKeys: ["test1", "test2"], + }); + + expect(statePersistent).toBeInstanceOf(StatePersistent); + expect(statePersistent.instantiatePersistent).toHaveBeenCalledWith({ + key: "statePersistentKey", + storageKeys: ["test1", "test2"], + }); + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + + expect(statePersistent._key).toBe(StatePersistent.placeHolderKey); + expect(statePersistent.ready).toBeFalsy(); + expect(statePersistent.isPersisted).toBeFalsy(); + expect(statePersistent.onLoad).toBeUndefined(); + expect(statePersistent.storageKeys).toStrictEqual([]); + expect(statePersistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create StatePersistent and should call initialLoading if Persistent is ready (default config)", () => { + // Overwrite instantiatePersistent once to not call it + jest + .spyOn(StatePersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = true; + }); + + const statePersistent = new StatePersistent(dummyState); + + expect(statePersistent.initialLoading).toHaveBeenCalled(); + }); + + it("should create StatePersistent and shouldn't call initialLoading if Persistent is ready (config.instantiate = false)", () => { + // Overwrite instantiatePersistent once to not call it and set ready property + jest + .spyOn(StatePersistent.prototype, "instantiatePersistent") + .mockImplementationOnce(function () { + this.ready = true; + }); + + const statePersistent = new StatePersistent(dummyState, { + instantiate: false, + }); + + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + }); + + describe("StatePersistent Function Tests", () => { + let statePersistent: StatePersistent; + + beforeEach(() => { + statePersistent = new StatePersistent(dummyState, { + key: "statePersistentKey", + storageKeys: ["dummyStorage"], + }); + dummyAgile.registerStorage( + new Storage({ + key: "dummyStorage", + methods: { + get: jest.fn(), + remove: jest.fn(), + set: jest.fn(), + }, + }) + ); + }); + + describe("setKey function tests", () => { + beforeEach(() => { + statePersistent.removePersistedValue = jest.fn(); + statePersistent.persistValue = jest.fn(); + statePersistent.initialLoading = jest.fn(); + jest.spyOn(statePersistent, "validatePersistent"); + }); + + it("should update key with valid key in ready Persistent", async () => { + statePersistent.ready = true; + statePersistent._key = "dummyKey"; + + await statePersistent.setKey("newKey"); + + expect(statePersistent._key).toBe("newKey"); + expect(statePersistent.ready).toBeTruthy(); + expect(statePersistent.validatePersistent).toHaveBeenCalled(); + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + expect(statePersistent.persistValue).toHaveBeenCalledWith("newKey"); + expect(statePersistent.removePersistedValue).toHaveBeenCalledWith( + "dummyKey" + ); + }); + + it("should update key with not valid key in ready Persistent", async () => { + statePersistent.ready = true; + statePersistent._key = "dummyKey"; + + await statePersistent.setKey(); + + expect(statePersistent._key).toBe(StatePersistent.placeHolderKey); + expect(statePersistent.ready).toBeFalsy(); + expect(statePersistent.validatePersistent).toHaveBeenCalled(); + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + expect(statePersistent.persistValue).not.toHaveBeenCalled(); + expect(statePersistent.removePersistedValue).toHaveBeenCalledWith( + "dummyKey" + ); + }); + + it("should update key with valid key in not ready Persistent", async () => { + statePersistent.ready = false; + + await statePersistent.setKey("newKey"); + + expect(statePersistent._key).toBe("newKey"); + expect(statePersistent.ready).toBeTruthy(); + expect(statePersistent.validatePersistent).toHaveBeenCalled(); + expect(statePersistent.initialLoading).toHaveBeenCalled(); + expect(statePersistent.persistValue).not.toHaveBeenCalled(); + expect(statePersistent.removePersistedValue).not.toHaveBeenCalled(); + }); + + it("should update key with not valid key in not ready Persistent", async () => { + statePersistent.ready = false; + + await statePersistent.setKey(); + + expect(statePersistent._key).toBe(StatePersistent.placeHolderKey); + expect(statePersistent.ready).toBeFalsy(); + expect(statePersistent.validatePersistent).toHaveBeenCalled(); + expect(statePersistent.initialLoading).not.toHaveBeenCalled(); + expect(statePersistent.persistValue).not.toHaveBeenCalled(); + expect(statePersistent.removePersistedValue).not.toHaveBeenCalled(); + }); + }); + + describe("initialLoading function tests", () => { + beforeEach(() => { + jest.spyOn(Persistent.prototype, "initialLoading"); + }); + + it("should initialLoad and set isPersisted in State to true", async () => { + await statePersistent.initialLoading(); + + expect(Persistent.prototype.initialLoading).toHaveBeenCalled(); + expect(dummyState.isPersisted).toBeTruthy(); + }); + }); + + describe("loadPersistedValue function tests", () => { + beforeEach(() => { + dummyState.set = jest.fn(); + statePersistent.persistValue = jest.fn(); + }); + + it("should load State Value with persistentKey and apply it to the State if loading was successful", async () => { + statePersistent.ready = true; + dummyAgile.storages.get = jest.fn(() => + Promise.resolve("dummyValue" as any) + ); + + const response = await statePersistent.loadPersistedValue(); + + expect(response).toBeTruthy(); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + statePersistent._key, + statePersistent.defaultStorageKey + ); + expect(dummyState.set).toHaveBeenCalledWith("dummyValue", { + storage: false, + }); + expect(statePersistent.persistValue).toHaveBeenCalledWith( + statePersistent._key + ); + }); + + it("should load State Value with persistentKey and shouldn't apply it to the State if loading wasn't successful", async () => { + statePersistent.ready = true; + dummyAgile.storages.get = jest.fn(() => + Promise.resolve(undefined as any) + ); + + const response = await statePersistent.loadPersistedValue(); + + expect(response).toBeFalsy(); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + statePersistent._key, + statePersistent.defaultStorageKey + ); + expect(dummyState.set).not.toHaveBeenCalled(); + expect(statePersistent.persistValue).not.toHaveBeenCalled(); + }); + + it("should load State Value with specific Key and apply it to the State if loading was successful", async () => { + statePersistent.ready = true; + dummyAgile.storages.get = jest.fn(() => + Promise.resolve("dummyValue" as any) + ); + + const response = await statePersistent.loadPersistedValue("coolKey"); + + expect(response).toBeTruthy(); + expect(dummyAgile.storages.get).toHaveBeenCalledWith( + "coolKey", + statePersistent.defaultStorageKey + ); + expect(dummyState.set).toHaveBeenCalledWith("dummyValue", { + storage: false, + }); + expect(statePersistent.persistValue).toHaveBeenCalledWith("coolKey"); + }); + + it("shouldn't load State Value if Persistent isn't ready", async () => { + statePersistent.ready = false; + dummyAgile.storages.get = jest.fn(() => + Promise.resolve(undefined as any) + ); + + const response = await statePersistent.loadPersistedValue(); + + expect(response).toBeFalsy(); + expect(dummyAgile.storages.get).not.toHaveBeenCalled(); + expect(dummyState.set).not.toHaveBeenCalled(); + expect(statePersistent.persistValue).not.toHaveBeenCalled(); + }); + }); + + describe("persistValue function tests", () => { + beforeEach(() => { + dummyState.addSideEffect = jest.fn(); + statePersistent.rebuildStorageSideEffect = jest.fn(); + + statePersistent.isPersisted = false; + }); + + it("should persist State with persistentKey", async () => { + statePersistent.ready = true; + + const response = await statePersistent.persistValue(); + + expect(response).toBeTruthy(); + expect(dummyState.addSideEffect).toHaveBeenCalledWith( + StatePersistent.storeValueSideEffectKey, + expect.any(Function) + ); + expect(statePersistent.rebuildStorageSideEffect).toHaveBeenCalledWith( + dummyState, + statePersistent._key + ); + expect(statePersistent.isPersisted).toBeTruthy(); + }); + + it("should persist State with specific Key", async () => { + statePersistent.ready = true; + + const response = await statePersistent.persistValue("coolKey"); + + expect(response).toBeTruthy(); + expect(dummyState.addSideEffect).toHaveBeenCalledWith( + StatePersistent.storeValueSideEffectKey, + expect.any(Function) + ); + expect(statePersistent.rebuildStorageSideEffect).toHaveBeenCalledWith( + dummyState, + "coolKey" + ); + expect(statePersistent.isPersisted).toBeTruthy(); + }); + + it("shouldn't persist State if Persistent isn't ready", async () => { + statePersistent.ready = false; + + const response = await statePersistent.persistValue(); + + expect(response).toBeFalsy(); + expect(dummyState.addSideEffect).not.toHaveBeenCalled(); + expect(statePersistent.rebuildStorageSideEffect).not.toHaveBeenCalled(); + expect(statePersistent.isPersisted).toBeFalsy(); + }); + + describe("test added sideEffect called StatePersistent.storeValueSideEffectKey", () => { + beforeEach(() => { + statePersistent.rebuildStorageSideEffect = jest.fn(); + }); + + it("should call rebuildStorageSideEffect", async () => { + await statePersistent.persistValue(); + + dummyState.sideEffects[StatePersistent.storeValueSideEffectKey]({ + dummy: "property", + }); + + expect(statePersistent.rebuildStorageSideEffect).toHaveBeenCalledWith( + dummyState, + statePersistent._key, + { + dummy: "property", + } + ); + }); + }); + }); + + describe("removePersistedValue function tests", () => { + beforeEach(() => { + dummyState.removeSideEffect = jest.fn(); + dummyAgile.storages.remove = jest.fn(); + + statePersistent.isPersisted = true; + }); + + it("should remove persisted State from Storage with persistentKey", async () => { + statePersistent.ready = true; + + const response = await statePersistent.removePersistedValue(); + + expect(response).toBeTruthy(); + expect(dummyState.removeSideEffect).toHaveBeenCalledWith( + StatePersistent.storeValueSideEffectKey + ); + expect(dummyAgile.storages.remove).toHaveBeenCalledWith( + statePersistent._key, + statePersistent.storageKeys + ); + expect(statePersistent.isPersisted).toBeFalsy(); + }); + + it("should remove persisted State from Storage with specific Key", async () => { + statePersistent.ready = true; + + const response = await statePersistent.removePersistedValue("coolKey"); + + expect(response).toBeTruthy(); + expect(dummyState.removeSideEffect).toHaveBeenCalledWith( + StatePersistent.storeValueSideEffectKey + ); + expect(dummyAgile.storages.remove).toHaveBeenCalledWith( + "coolKey", + statePersistent.storageKeys + ); + expect(statePersistent.isPersisted).toBeFalsy(); + }); + + it("shouldn't remove State from Storage if Persistent isn't ready", async () => { + statePersistent.ready = false; + + const response = await statePersistent.removePersistedValue("coolKey"); + + expect(response).toBeFalsy(); + expect(dummyState.removeSideEffect).not.toHaveBeenCalled(); + expect(dummyAgile.storages.remove).not.toHaveBeenCalled(); + expect(statePersistent.isPersisted).toBeTruthy(); + }); + }); + + describe("formatKey function tests", () => { + it("should return key of State if no key got passed", () => { + dummyState._key = "coolKey"; + + const response = statePersistent.formatKey(); + + expect(response).toBe("coolKey"); + }); + + it("should return passed key", () => { + dummyState._key = "coolKey"; + + const response = statePersistent.formatKey("awesomeKey"); + + expect(response).toBe("awesomeKey"); + }); + + it("should return and apply passed key to State if State had no own key before", () => { + dummyState._key = undefined; + + const response = statePersistent.formatKey("awesomeKey"); + + expect(response).toBe("awesomeKey"); + expect(dummyState._key).toBe("awesomeKey"); + }); + + it("should return undefined if no key got passed and State has no key", () => { + dummyState._key = undefined; + + const response = statePersistent.formatKey(); + + expect(response).toBeUndefined(); + }); + }); + + describe("rebuildStorageSideEffect function tests", () => { + beforeEach(() => { + dummyAgile.storages.set = jest.fn(); + }); + + it("should save State Value in Storage (default config)", () => { + statePersistent.rebuildStorageSideEffect(dummyState, "coolKey"); + + expect(dummyAgile.storages.set).toHaveBeenCalledWith( + "coolKey", + dummyState.getPersistableValue(), + statePersistent.storageKeys + ); + }); + + it("shouldn't save State Value in Storage (config.storage = false)", () => { + statePersistent.rebuildStorageSideEffect(dummyState, "coolKey", { + storage: false, + }); + + expect(dummyAgile.storages.set).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/state/state.runtime.job.test.ts b/packages/core/tests/unit/state/state.runtime.job.test.ts new file mode 100644 index 00000000..9e57bf58 --- /dev/null +++ b/packages/core/tests/unit/state/state.runtime.job.test.ts @@ -0,0 +1,104 @@ +import { + Agile, + RuntimeJob, + StateObserver, + StateRuntimeJob, + State, + Integration, +} from "../../../src"; + +// jest.mock("../../../src/runtime/runtime.job"); // Can't mock RuntimeJob because mocks get instantiated before everything else -> I got the good old not loaded Object error https://github.com/kentcdodds/how-jest-mocking-works + +describe("RuntimeJob Tests", () => { + let dummyAgile: Agile; + let dummyIntegration: Integration; + let dummyState: State; + let dummyObserver: StateObserver; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + dummyIntegration = new Integration({ + key: "myIntegration", + }); + dummyState = new State(dummyAgile, "dummyValue"); + dummyObserver = new StateObserver(dummyState); + }); + + it("should create RuntimeJob with Agile that has integrations (default config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has integrations (specific config)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver, { + key: "dummyJob", + sideEffects: false, + force: true, + }); + + expect(job._key).toBe("dummyJob"); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: false, + force: true, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeTruthy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob with Agile that has no integrations (default config)", () => { + const job = new StateRuntimeJob(dummyObserver); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: false, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); + + it("should create RuntimeJob and Agile that has integrations (config.background = true)", () => { + dummyAgile.integrate(dummyIntegration); + + const job = new StateRuntimeJob(dummyObserver, { background: true }); + + expect(job._key).toBeUndefined(); + expect(job.observer).toBe(dummyObserver); + expect(job.config).toStrictEqual({ + background: true, + sideEffects: true, + force: false, + storage: true, + overwrite: false, + }); + expect(job.rerender).toBeFalsy(); + expect(job.performed).toBeFalsy(); + expect(job.subscriptionContainersToUpdate.size).toBe(0); + }); +}); diff --git a/packages/core/tests/unit/state/state.test.ts b/packages/core/tests/unit/state/state.test.ts new file mode 100644 index 00000000..a1717ea7 --- /dev/null +++ b/packages/core/tests/unit/state/state.test.ts @@ -0,0 +1,884 @@ +import { + State, + Agile, + StateObserver, + Observer, + StatePersistent, + ComputedTracker, +} from "../../../src"; +import * as Utils from "../../../src/utils"; + +jest.mock("../../../src/state/state.persistent"); + +describe("State Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyAgile = new Agile({ localStorage: false }); + + jest.spyOn(State.prototype, "set"); + console.error = jest.fn(); + console.warn = jest.fn(); + }); + + it("should create State and should call initial set (default config)", () => { + // Overwrite select once to not call it + jest.spyOn(State.prototype, "set").mockReturnValueOnce(undefined); + + const state = new State(dummyAgile, "coolValue"); + + expect(state.set).toHaveBeenCalledWith("coolValue", { overwrite: true }); + expect(state._key).toBeUndefined(); + expect(state.valueType).toBeUndefined(); + expect(state.isSet).toBeFalsy(); + expect(state.isPlaceholder).toBeTruthy(); + expect(state.initialStateValue).toBe("coolValue"); + expect(state._value).toBe("coolValue"); + expect(state.previousStateValue).toBe("coolValue"); + expect(state.nextStateValue).toBe("coolValue"); + expect(state.observer).toBeInstanceOf(StateObserver); + expect(state.observer.deps.size).toBe(0); + expect(state.observer._key).toBeUndefined(); + expect(state.sideEffects).toStrictEqual({}); + expect(state.computeMethod).toBeUndefined(); + expect(state.isPersisted).toBeFalsy(); + expect(state.persistent).toBeUndefined(); + expect(state.watchers).toStrictEqual({}); + }); + + it("should create State and should call initial set (specific config)", () => { + // Overwrite select once to not call it + jest.spyOn(State.prototype, "set").mockReturnValueOnce(undefined); + + const dummyObserver = new Observer(dummyAgile); + + const state = new State(dummyAgile, "coolValue", { + key: "coolState", + deps: [dummyObserver], + }); + + expect(state.set).toHaveBeenCalledWith("coolValue", { overwrite: true }); + expect(state._key).toBe("coolState"); + expect(state.valueType).toBeUndefined(); + expect(state.isSet).toBeFalsy(); + expect(state.isPlaceholder).toBeTruthy(); + expect(state.initialStateValue).toBe("coolValue"); + expect(state._value).toBe("coolValue"); + expect(state.previousStateValue).toBe("coolValue"); + expect(state.nextStateValue).toBe("coolValue"); + expect(state.observer).toBeInstanceOf(StateObserver); + expect(state.observer.deps.size).toBe(1); + expect(state.observer.deps.has(dummyObserver)).toBeTruthy(); + expect(state.observer._key).toBe("coolState"); + expect(state.sideEffects).toStrictEqual({}); + expect(state.computeMethod).toBeUndefined(); + expect(state.isPersisted).toBeFalsy(); + expect(state.persistent).toBeUndefined(); + expect(state.watchers).toStrictEqual({}); + }); + + it("should create State and shouldn't call initial set (config.isPlaceholder = true)", () => { + // Overwrite select once to not call it + jest.spyOn(State.prototype, "set").mockReturnValueOnce(undefined); + + const state = new State(dummyAgile, "coolValue", { isPlaceholder: true }); + + expect(state.set).not.toHaveBeenCalled(); + expect(state._key).toBeUndefined(); + expect(state.valueType).toBeUndefined(); + expect(state.isSet).toBeFalsy(); + expect(state.isPlaceholder).toBeTruthy(); + expect(state.initialStateValue).toBe("coolValue"); + expect(state._value).toBe("coolValue"); + expect(state.previousStateValue).toBe("coolValue"); + expect(state.nextStateValue).toBe("coolValue"); + expect(state.observer).toBeInstanceOf(StateObserver); + expect(state.observer.deps.size).toBe(0); + expect(state.observer._key).toBeUndefined(); + expect(state.sideEffects).toStrictEqual({}); + expect(state.computeMethod).toBeUndefined(); + expect(state.isPersisted).toBeFalsy(); + expect(state.persistent).toBeUndefined(); + expect(state.watchers).toStrictEqual({}); + }); + + describe("State Function Tests", () => { + let numberState: State; + let objectState: State<{ name: string; age: number }>; + let arrayState: State; + let booleanState: State; + + beforeEach(() => { + numberState = new State(dummyAgile, 10, { + key: "numberStateKey", + }); + objectState = new State<{ name: string; age: number }>( + dummyAgile, + { name: "jeff", age: 10 }, + { + key: "objectStateKey", + } + ); + arrayState = new State(dummyAgile, ["jeff"], { + key: "arrayStateKey", + }); + booleanState = new State(dummyAgile, false, { + key: "booleanStateKey", + }); + }); + + describe("value set function tests", () => { + it("should call set function with passed value", () => { + numberState.set = jest.fn(); + + numberState.value = 20; + + expect(numberState.set).toHaveBeenCalledWith(20); + }); + }); + + describe("value get function tests", () => { + it("should return current value", () => { + expect(numberState.value).toBe(10); + ComputedTracker.tracked = jest.fn(); + }); + + it("should return current value", () => { + const value = numberState.value; + + expect(value).toBe(10); + expect(ComputedTracker.tracked).toHaveBeenCalledWith( + numberState.observer + ); + }); + }); + + describe("key set function tests", () => { + it("should call setKey with passed value", () => { + numberState.setKey = jest.fn(); + + numberState.key = "newKey"; + + expect(numberState.setKey).toHaveBeenCalledWith("newKey"); + }); + }); + + describe("key get function tests", () => { + it("should return current State Key", () => { + expect(numberState.key).toBe("numberStateKey"); + }); + }); + + describe("setKey function tests", () => { + beforeEach(() => { + numberState.persistent = new StatePersistent(numberState); + + numberState.persistent.setKey = jest.fn(); + }); + + it("should update existing Key in all instances", () => { + numberState.persistent._key = "numberStateKey"; + + numberState.setKey("newKey"); + + expect(numberState._key).toBe("newKey"); + expect(numberState.observer._key).toBe("newKey"); + expect(numberState.persistent.setKey).toHaveBeenCalledWith("newKey"); + }); + + it("should update existing Key in all instances except persistent if the StateKey and PersistKey aren't equal", () => { + numberState.persistent._key = "randomKey"; + + numberState.setKey("newKey"); + + expect(numberState._key).toBe("newKey"); + expect(numberState.observer._key).toBe("newKey"); + expect(numberState.persistent.setKey).not.toHaveBeenCalled(); + }); + + it("should update existing Key in all instances except persistent if new StateKey is undefined", () => { + numberState.persistent._key = "numberStateKey"; + + numberState.setKey(undefined); + + expect(numberState._key).toBeUndefined(); + expect(numberState.observer._key).toBeUndefined(); + expect(numberState.persistent.setKey).not.toHaveBeenCalled(); + }); + }); + + describe("set function tests", () => { + beforeEach(() => { + jest.spyOn(numberState.observer, "ingestValue"); + }); + + it("should ingestValue if value has correct type (default config)", () => { + numberState.set(20); + + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error).not.toHaveBeenCalled(); + expect(numberState.observer.ingestValue).toHaveBeenCalledWith(20, { + sideEffects: true, + background: false, + force: false, + storage: true, + overwrite: false, + }); + }); + + it("should ingestValue if value has correct type (specific config)", () => { + numberState.set(20, { + sideEffects: false, + background: true, + storage: false, + }); + + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error).not.toHaveBeenCalled(); + expect(numberState.observer.ingestValue).toHaveBeenCalledWith(20, { + sideEffects: false, + background: true, + force: false, + storage: false, + overwrite: false, + }); + }); + + it("shouldn't ingestValue if value hasn't correct type (default config)", () => { + numberState.type(Number); + + numberState.set("coolValue" as any); + + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Incorrect type (string) was provided." + ); + expect(numberState.observer.ingestValue).not.toHaveBeenCalled(); + }); + + it("should ingestValue if value hasn't correct type (config.force = true)", () => { + numberState.type(Number); + + numberState.set("coolValue" as any, { force: true }); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Incorrect type (string) was provided." + ); + expect(console.error).not.toHaveBeenCalled(); + expect(numberState.observer.ingestValue).toHaveBeenCalledWith( + "coolValue", + { + sideEffects: true, + background: false, + force: true, + storage: true, + overwrite: false, + } + ); + }); + + it("should ingestValue if value hasn't correct type but the type isn't explicit defined (default config)", () => { + numberState.set("coolValue" as any); + + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error).not.toHaveBeenCalled(); + expect(numberState.observer.ingestValue).toHaveBeenCalledWith( + "coolValue", + { + sideEffects: true, + background: false, + force: false, + storage: true, + overwrite: false, + } + ); + }); + }); + + describe("ingest function tests", () => { + beforeEach(() => { + numberState.observer.ingest = jest.fn(); + }); + + it("should call ingest function in Observer (default config)", () => { + numberState.ingest(); + + expect(numberState.observer.ingest).toHaveBeenCalledWith({}); + }); + + it("should call ingest function in Observer (specific config)", () => { + numberState.ingest({ + force: true, + background: true, + }); + + expect(numberState.observer.ingest).toHaveBeenCalledWith({ + background: true, + force: true, + }); + }); + }); + + describe("type function tests", () => { + it("should assign valid Type to State", () => { + numberState.type(Number); + + expect(numberState.valueType).toBe("number"); + }); + + it("shouldn't assign invalid Type to State", () => { + numberState.type("fuckingType"); + + expect(numberState.valueType).toBeUndefined(); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: 'fuckingType' is not supported! Supported types: String, Boolean, Array, Object, Number" + ); + }); + }); + + describe("undo function tests", () => { + beforeEach(() => { + numberState.set = jest.fn(); + }); + + it("should assign previousStateValue to currentValue (default config)", () => { + numberState.previousStateValue = 99; + + numberState.undo(); + + expect(numberState.set).toHaveBeenCalledWith( + numberState.previousStateValue, + {} + ); + }); + + it("should assign previousStateValue to currentValue (specific config)", () => { + numberState.previousStateValue = 99; + + numberState.undo({ + force: true, + storage: false, + }); + + expect(numberState.set).toHaveBeenCalledWith( + numberState.previousStateValue, + { + force: true, + storage: false, + } + ); + }); + }); + + describe("reset function tests", () => { + beforeEach(() => { + numberState.set = jest.fn(); + }); + + it("should assign initialStateValue to currentValue (default config)", () => { + numberState.initialStateValue = 99; + + numberState.reset(); + + expect(numberState.set).toHaveBeenCalledWith( + numberState.initialStateValue, + {} + ); + }); + + it("should assign initialStateValue to currentValue (specific config)", () => { + numberState.initialStateValue = 99; + + numberState.reset({ + force: true, + storage: false, + }); + + expect(numberState.set).toHaveBeenCalledWith( + numberState.initialStateValue, + { + force: true, + storage: false, + } + ); + }); + }); + + describe("patch function tests", () => { + beforeEach(() => { + objectState.ingest = jest.fn(); + numberState.ingest = jest.fn(); + jest.spyOn(Utils, "flatMerge"); + }); + + it("shouldn't patch and ingest passed object based value into a not object based State (default config)", () => { + numberState.patch({ changed: "object" }); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: You can't use the patch method on a non object based States!" + ); + expect(objectState.ingest).not.toHaveBeenCalled(); + }); + + it("shouldn't patch and ingest passed not object based value into object based State (default config)", () => { + objectState.patch("number" as any); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: TargetWithChanges has to be an Object!" + ); + expect(objectState.ingest).not.toHaveBeenCalled(); + }); + + it("should patch and ingest passed object based value into a object based State (default config)", () => { + objectState.patch({ name: "frank" }); + + expect(Utils.flatMerge).toHaveBeenCalledWith( + { age: 10, name: "jeff" }, + { name: "frank" }, + { + addNewProperties: true, + } + ); + expect(objectState.nextStateValue).toStrictEqual({ + age: 10, + name: "frank", + }); + expect(objectState.ingest).toHaveBeenCalledWith({ + background: false, + force: false, + overwrite: false, + sideEffects: true, + storage: true, + }); + }); + + it("should patch and ingest passed object based value into a object based State (specific config)", () => { + objectState.patch( + { name: "frank" }, + { + addNewProperties: false, + background: true, + force: true, + overwrite: true, + sideEffects: false, + } + ); + + expect(Utils.flatMerge).toHaveBeenCalledWith( + { age: 10, name: "jeff" }, + { name: "frank" }, + { + addNewProperties: false, + } + ); + expect(objectState.nextStateValue).toStrictEqual({ + age: 10, + name: "frank", + }); + expect(objectState.ingest).toHaveBeenCalledWith({ + background: true, + force: true, + overwrite: true, + sideEffects: false, + storage: true, + }); + }); + }); + + describe("watch function tests", () => { + const dummyCallbackFunction1 = () => {}; + const dummyCallbackFunction2 = () => {}; + + it("should add passed watcherFunction to watchers at passed key", () => { + const response = numberState.watch("dummyKey", dummyCallbackFunction1); + + expect(response).toBe(numberState); + expect(numberState.watchers).toHaveProperty("dummyKey"); + expect(numberState.watchers["dummyKey"]).toBe(dummyCallbackFunction1); + }); + + it("should add passed watcherFunction to watchers at random key if no key passed and return that generated key", () => { + jest.spyOn(Utils, "generateId").mockReturnValue("randomKey"); + + const response = numberState.watch(dummyCallbackFunction1); + + expect(response).toBe("randomKey"); + expect(numberState.watchers).toHaveProperty("randomKey"); + expect(numberState.watchers["randomKey"]).toBe(dummyCallbackFunction1); + expect(Utils.generateId).toHaveBeenCalled(); + }); + + it("shouldn't add passed invalid watcherFunction to watchers at passed key", () => { + const response = numberState.watch( + "dummyKey", + "noFunction hehe" as any + ); + + expect(response).toBe(numberState); + expect(numberState.watchers).not.toHaveProperty("dummyKey"); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: A Watcher Callback Function has to be typeof Function!" + ); + }); + + it("shouldn't add passed watcherFunction to watchers at passed key if passed key is already occupied", () => { + numberState.watchers["dummyKey"] = dummyCallbackFunction2; + + const response = numberState.watch("dummyKey", dummyCallbackFunction1); + + expect(response).toBe(numberState); + expect(numberState.watchers).toHaveProperty("dummyKey"); + expect(numberState.watchers["dummyKey"]).toBe(dummyCallbackFunction2); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Watcher Callback Function with the key/name 'dummyKey' already exists!" + ); + }); + }); + + describe("removeWatcher function tests", () => { + beforeEach(() => { + numberState.watchers["dummyKey"] = () => {}; + }); + + it("should remove watcher at key from State", () => { + numberState.removeWatcher("dummyKey"); + + expect(numberState.watchers).not.toHaveProperty("dummyKey"); + }); + }); + + describe("onInaugurated function tests", () => { + let dummyCallbackFunction; + + beforeEach(() => { + jest.spyOn(numberState, "watch"); + dummyCallbackFunction = jest.fn(); + }); + + it("should add watcher called InauguratedWatcherKey to State that destroys it self after it got called", () => { + numberState.onInaugurated(dummyCallbackFunction); + + expect(numberState.watch).toHaveBeenCalledWith( + "InauguratedWatcherKey", + expect.any(Function) + ); + expect(numberState.watchers).toHaveProperty("InauguratedWatcherKey"); + }); + + it("should remove itself after getting called", () => { + numberState.onInaugurated(dummyCallbackFunction); + + // Call Inaugurated Watcher + numberState.watchers["InauguratedWatcherKey"](10); + + expect(dummyCallbackFunction).toHaveBeenCalledWith(10); + expect(numberState.watchers).not.toHaveProperty( + "InauguratedWatcherKey" + ); + }); + }); + + describe("hasWatcher function tests", () => { + beforeEach(() => { + numberState.watchers["dummyKey"] = () => {}; + }); + + it("should return true if Watcher at given Key exists", () => { + expect(numberState.hasWatcher("dummyKey")).toBeTruthy(); + }); + + it("should return false if Watcher at given Key doesn't exists", () => { + expect(numberState.hasWatcher("notExistingDummyKey")).toBeFalsy(); + }); + }); + + describe("persist function tests", () => { + it("should create persistent with StateKey (default config)", () => { + numberState.persist(); + + expect(numberState.persistent).toBeInstanceOf(StatePersistent); + expect(StatePersistent).toHaveBeenCalledWith(numberState, { + instantiate: true, + storageKeys: [], + key: numberState._key, + }); + }); + + it("should create persistent with StateKey (specific config)", () => { + numberState.persist({ + storageKeys: ["test1", "test2"], + instantiate: false, + }); + + expect(numberState.persistent).toBeInstanceOf(StatePersistent); + expect(StatePersistent).toHaveBeenCalledWith(numberState, { + instantiate: false, + storageKeys: ["test1", "test2"], + key: numberState._key, + }); + }); + + it("should create persistent with passed Key (default config)", () => { + numberState.persist("passedKey"); + + expect(numberState.persistent).toBeInstanceOf(StatePersistent); + expect(StatePersistent).toHaveBeenCalledWith(numberState, { + instantiate: true, + storageKeys: [], + key: "passedKey", + }); + }); + + it("should create persistent with passed Key (specific config)", () => { + numberState.persist("passedKey", { + storageKeys: ["test1", "test2"], + instantiate: false, + }); + + expect(numberState.persistent).toBeInstanceOf(StatePersistent); + expect(StatePersistent).toHaveBeenCalledWith(numberState, { + instantiate: false, + storageKeys: ["test1", "test2"], + key: "passedKey", + }); + }); + + it("should overwrite existing persistent with a warning", () => { + numberState.persistent = new StatePersistent(numberState); + + numberState.persist("newPersistentKey"); + + expect(numberState.persistent).toBeInstanceOf(StatePersistent); + // expect(numberState.persistent._key).toBe("newPersistentKey"); // Can not test because of Mocking Persistent + expect(StatePersistent).toHaveBeenCalledWith(numberState, { + instantiate: true, + storageKeys: [], + key: "newPersistentKey", + }); + expect(console.warn).toHaveBeenCalledWith( + `Agile Warn: By persisting the State '${numberState._key}' twice you overwrite the old Persistent Instance!` + ); + }); + }); + + describe("onLoad function tests", () => { + const dummyCallbackFunction = jest.fn(); + + it("should set onLoad function if State is persisted and shouldn't call it initially (state.isPersisted = false)", () => { + numberState.persistent = new StatePersistent(numberState); + numberState.isPersisted = false; + + numberState.onLoad(dummyCallbackFunction); + + expect(numberState.persistent.onLoad).toBe(dummyCallbackFunction); + expect(dummyCallbackFunction).not.toHaveBeenCalled(); + }); + + it("should set onLoad function if State is persisted and should call it initially (state.isPersisted = true)", () => { + numberState.persistent = new StatePersistent(numberState); + numberState.isPersisted = true; + + numberState.onLoad(dummyCallbackFunction); + + expect(numberState.persistent.onLoad).toBe(dummyCallbackFunction); + expect(dummyCallbackFunction).toHaveBeenCalledWith(true); + }); + + it("shouldn't set onLoad function if State isn't persisted and should drop a error", () => { + numberState.onLoad(dummyCallbackFunction); + + expect(dummyCallbackFunction).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Please make sure you persist the State 'numberStateKey' before using the 'onLoad' function!" + ); + }); + }); + + describe("copy function tests", () => { + it("should return a reference free copy of the current State Value", () => { + jest.spyOn(Utils, "copy"); + const value = numberState.copy(); + + expect(value).toBe(10); + expect(Utils.copy).toHaveBeenCalledWith(10); + }); + }); + + describe("exists get function tests", () => { + it("should return true if value isn't undefined and State is no placeholder", () => { + expect(numberState.exists).toBeTruthy(); + }); + + it("should return false if value is undefined and State is no placeholder", () => { + numberState._value = undefined; + + expect(numberState.exists).toBeFalsy(); + }); + + it("should return false if value isn't undefined and State is placeholder", () => { + numberState.isPlaceholder = true; + + expect(numberState.exists).toBeFalsy(); + }); + }); + + describe("is function tests", () => { + beforeEach(() => { + jest.spyOn(Utils, "equal"); + }); + + it("should return true if passed value is equal to the current StateValue", () => { + const response = numberState.is(10); + + expect(response).toBeTruthy(); + expect(Utils.equal).toHaveBeenCalledWith(10, numberState._value); + }); + + it("should return false if passed value is not equal to the current StateValue", () => { + const response = numberState.is(20); + + expect(response).toBeFalsy(); + expect(Utils.equal).toHaveBeenCalledWith(20, numberState._value); + }); + }); + + describe("isNot function tests", () => { + beforeEach(() => { + jest.spyOn(Utils, "notEqual"); + }); + + it("should return false if passed value is equal to the current StateValue", () => { + const response = numberState.isNot(10); + + expect(response).toBeFalsy(); + expect(Utils.notEqual).toHaveBeenCalledWith(10, numberState._value); + }); + + it("should return true if passed value is not equal to the current StateValue", () => { + const response = numberState.isNot(20); + + expect(response).toBeTruthy(); + expect(Utils.notEqual).toHaveBeenCalledWith(20, numberState._value); + }); + }); + + describe("invert function tests", () => { + beforeEach(() => { + numberState.set = jest.fn(); + booleanState.set = jest.fn(); + }); + + it("should invert current value of a boolean based State", () => { + booleanState.invert(); + + expect(booleanState.set).toHaveBeenCalledWith(true); + }); + + it("shouldn't invert current value if not boolean based State and should print a error", () => { + numberState.invert(); + + expect(numberState.set).not.toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: You can only invert boolean based States!" + ); + }); + }); + + describe("compute function tests", () => { + it("should assign passed function to computeMethod", () => { + const computeMethod = () => 10; + + numberState.compute(computeMethod); + + expect(numberState.computeMethod).toBe(computeMethod); + }); + + it("shouldn't assign passed invalid function to computeMethod", () => { + numberState.compute(10 as any); + + expect(numberState.computeMethod).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: A computeMethod has to be a function!" + ); + }); + }); + + describe("addSideEffect function tests", () => { + const sideEffectFunction = () => {}; + + it("should add passed function to sideEffects at passed key", () => { + numberState.addSideEffect("dummyKey", sideEffectFunction); + + expect(numberState.sideEffects).toHaveProperty("dummyKey"); + expect(numberState.sideEffects["dummyKey"]).toBe(sideEffectFunction); + }); + + it("shouldn't add passed invalid function to sideEffects at passed key", () => { + numberState.addSideEffect("dummyKey", 10 as any); + + expect(numberState.sideEffects).not.toHaveProperty("dummyKey"); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: A sideEffect function has to be a function!" + ); + }); + }); + + describe("removeSideEffect function tests", () => { + beforeEach(() => { + numberState.sideEffects["dummyKey"] = jest.fn(); + }); + + it("should remove sideEffect at key from State", () => { + numberState.removeSideEffect("dummyKey"); + + expect(numberState.sideEffects).not.toHaveProperty("dummyKey"); + }); + }); + + describe("hasSideEffect function tests", () => { + beforeEach(() => { + numberState.sideEffects["dummyKey"] = jest.fn(); + }); + + it("should return true if SideEffect at given Key exists", () => { + expect(numberState.hasSideEffect("dummyKey")).toBeTruthy(); + }); + + it("should return false if SideEffect at given Key doesn't exists", () => { + expect(numberState.hasSideEffect("notExistingDummyKey")).toBeFalsy(); + }); + }); + + describe("hasCorrectType function tests", () => { + it("should return true if State Type matches passed type", () => { + numberState.type(Number); + + expect(numberState.hasCorrectType(10)).toBeTruthy(); + }); + + it("should return false if State Type doesn't matches passed type", () => { + numberState.type(Number); + + expect(numberState.hasCorrectType("stringValue")).toBeFalsy(); + }); + + it("should return true if State has no defined Type", () => { + expect(numberState.hasCorrectType("stringValue")).toBeTruthy(); + }); + }); + + describe("getPublicValue function tests", () => { + it("should return value of State", () => { + expect(numberState.getPublicValue()).toBe(10); + }); + + it("should return output of State", () => { + numberState["output"] = 99; + + expect(numberState.getPublicValue()).toBe(99); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/storages/persistent.test.ts b/packages/core/tests/unit/storages/persistent.test.ts new file mode 100644 index 00000000..1937aba3 --- /dev/null +++ b/packages/core/tests/unit/storages/persistent.test.ts @@ -0,0 +1,291 @@ +import { Agile, Persistent, Storage } from "../../../src"; + +describe("Persistent Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyAgile = new Agile({ localStorage: false }); + + jest.spyOn(Persistent.prototype, "instantiatePersistent"); + console.error = jest.fn(); + }); + + it("should create Persistent (default config)", () => { + // Overwrite instantiatePersistent once to not call it + jest + .spyOn(Persistent.prototype, "instantiatePersistent") + .mockReturnValueOnce(undefined); + + const persistent = new Persistent(dummyAgile); + + expect(persistent).toBeInstanceOf(Persistent); + expect(persistent.instantiatePersistent).toHaveBeenCalledWith({ + storageKeys: [], + key: undefined, + }); + expect( + dummyAgile.storages.persistentInstances.has(persistent) + ).toBeTruthy(); + + expect(persistent._key).toBe(Persistent.placeHolderKey); + expect(persistent.ready).toBeFalsy(); + expect(persistent.isPersisted).toBeFalsy(); + expect(persistent.onLoad).toBeUndefined(); + expect(persistent.storageKeys).toStrictEqual([]); + expect(persistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create Persistent (specific config)", () => { + // Overwrite instantiatePersistent once to not call it + jest + .spyOn(Persistent.prototype, "instantiatePersistent") + .mockReturnValueOnce(undefined); + + const persistent = new Persistent(dummyAgile, { + storageKeys: ["test1", "test2"], + key: "persistentKey", + }); + + expect(persistent).toBeInstanceOf(Persistent); + expect(persistent.instantiatePersistent).toHaveBeenCalledWith({ + storageKeys: ["test1", "test2"], + key: "persistentKey", + }); + expect( + dummyAgile.storages.persistentInstances.has(persistent) + ).toBeTruthy(); + + expect(persistent._key).toBe(Persistent.placeHolderKey); + expect(persistent.ready).toBeFalsy(); + expect(persistent.isPersisted).toBeFalsy(); + expect(persistent.onLoad).toBeUndefined(); + expect(persistent.storageKeys).toStrictEqual([]); + expect(persistent.defaultStorageKey).toBeUndefined(); + }); + + it("should create Persistent (config.instantiate = false)", () => { + // Overwrite instantiatePersistent once to not call it + jest + .spyOn(Persistent.prototype, "instantiatePersistent") + .mockReturnValueOnce(undefined); + + const persistent = new Persistent(dummyAgile, { instantiate: false }); + + expect(persistent).toBeInstanceOf(Persistent); + expect(persistent.instantiatePersistent).not.toHaveBeenCalled(); + expect( + dummyAgile.storages.persistentInstances.has(persistent) + ).toBeTruthy(); + + expect(persistent._key).toBe(Persistent.placeHolderKey); + expect(persistent.ready).toBeFalsy(); + expect(persistent.isPersisted).toBeFalsy(); + expect(persistent.onLoad).toBeUndefined(); + expect(persistent.storageKeys).toStrictEqual([]); + expect(persistent.defaultStorageKey).toBeUndefined(); + }); + + describe("Persistent Function Tests", () => { + let persistent: Persistent; + + beforeEach(() => { + persistent = new Persistent(dummyAgile); + }); + + describe("key set function tests", () => { + it("should call setKey with passed value", () => { + persistent.setKey = jest.fn(); + + persistent.key = "dummyKey"; + + expect(persistent.setKey).toHaveBeenCalledWith("dummyKey"); + }); + }); + + describe("ket get function tests", () => { + it("should get key property of Persistent", () => { + persistent._key = "dummyKey"; + + expect(persistent.key).toBe("dummyKey"); + }); + }); + + describe("instantiatePersistent function tests", () => { + it("should call assign key to formatKey and call assignStorageKeys, validatePersistent", () => { + jest.spyOn(persistent, "formatKey"); + jest.spyOn(persistent, "assignStorageKeys"); + jest.spyOn(persistent, "validatePersistent"); + + persistent.instantiatePersistent({ + key: "persistentKey", + storageKeys: ["myName", "is", "jeff"], + }); + + expect(persistent._key).toBe("persistentKey"); + expect(persistent.formatKey).toHaveBeenCalledWith("persistentKey"); + expect(persistent.assignStorageKeys).toHaveBeenCalledWith([ + "myName", + "is", + "jeff", + ]); + expect(persistent.validatePersistent).toHaveBeenCalled(); + }); + }); + + describe("validatePersistent function tests", () => { + beforeEach(() => { + persistent.key = Persistent.placeHolderKey; + persistent.defaultStorageKey = undefined; + persistent.storageKeys = []; + persistent.ready = undefined; + }); + + it("should return false and print error if no set key and no set StorageKeys", () => { + const isValid = persistent.validatePersistent(); + + expect(isValid).toBeFalsy(); + expect(persistent.ready).toBeFalsy(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No valid persist Key found! Please provide a Key or assign one to the parent instance." + ); + }); + + it("should return false and print error if set key and no set StorageKeys", () => { + persistent._key = "persistentKey"; + + const isValid = persistent.validatePersistent(); + + expect(isValid).toBeFalsy(); + expect(persistent.ready).toBeFalsy(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No persist Storage Key found! Please provide at least one Storage Key." + ); + }); + + it("should return false if no set key and set StorageKeys", () => { + persistent.defaultStorageKey = "test"; + persistent.storageKeys = ["test"]; + + const isValid = persistent.validatePersistent(); + + expect(isValid).toBeFalsy(); + expect(persistent.ready).toBeFalsy(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No valid persist Key found! Please provide a Key or assign one to the parent instance." + ); + }); + + it("should return true if set key and set StorageKeys", () => { + persistent._key = "persistentKey"; + persistent.defaultStorageKey = "test"; + persistent.storageKeys = ["test"]; + + const isValid = persistent.validatePersistent(); + + expect(isValid).toBeTruthy(); + expect(persistent.ready).toBeTruthy(); + }); + }); + + describe("assignStorageKeys function tests", () => { + it("should assign passed StorageKeys and set first one as default StorageKey", () => { + persistent.assignStorageKeys(["test1", "test2", "test3"]); + + expect(persistent.storageKeys).toStrictEqual([ + "test1", + "test2", + "test3", + ]); + expect(persistent.defaultStorageKey).toBe("test1"); + }); + + it("should try to get default StorageKey from Agile if no StorageKeys passed", () => { + dummyAgile.storages.register( + new Storage({ + key: "storage1", + methods: { + get: (key) => {}, + set: (key, value) => {}, + remove: (key) => {}, + }, + }), + { default: true } + ); + + persistent.assignStorageKeys(); + + expect(persistent.storageKeys).toStrictEqual(["storage1"]); + expect(persistent.defaultStorageKey).toBe("storage1"); + }); + }); + + describe("initialLoading function tests", () => { + beforeEach(() => { + persistent.onLoad = jest.fn(); + persistent.loadPersistedValue = jest.fn(); + persistent.persistValue = jest.fn(); + }); + + it("shouldn't call persistValue if value got successful loaded", async () => { + persistent.loadPersistedValue = jest.fn(() => Promise.resolve(true)); + + await persistent.initialLoading(); + + expect(persistent.loadPersistedValue).toHaveBeenCalled(); + expect(persistent.persistValue).not.toHaveBeenCalled(); + expect(persistent.onLoad).toHaveBeenCalledWith(true); + }); + + it("should call persistValue if value doesn't got successful loaded", async () => { + persistent.loadPersistedValue = jest.fn(() => Promise.resolve(false)); + + await persistent.initialLoading(); + + expect(persistent.loadPersistedValue).toHaveBeenCalled(); + expect(persistent.persistValue).toHaveBeenCalled(); + expect(persistent.onLoad).toHaveBeenCalledWith(false); + }); + }); + + describe("loadPersistedValue function tests", () => { + it("should print error", () => { + persistent.loadPersistedValue(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: 'loadPersistedValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!" + ); + }); + }); + + describe("persistValue function tests", () => { + it("should print error", () => { + persistent.persistValue(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: 'persistValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!" + ); + }); + }); + + describe("removePersistedValue function tests", () => { + it("should print error", () => { + persistent.removePersistedValue(); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: 'removePersistedValue' function isn't Set in Persistent! Be aware that Persistent is no stand alone class!" + ); + }); + + describe("formatKey function tests", () => { + it("should return passed key", () => { + expect(persistent.formatKey("test")).toBe("test"); + }); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/storages/storage.test.ts b/packages/core/tests/unit/storages/storage.test.ts new file mode 100644 index 00000000..e2cf17cb --- /dev/null +++ b/packages/core/tests/unit/storages/storage.test.ts @@ -0,0 +1,338 @@ +import { Storage } from "../../../src"; + +describe("Storage Tests", () => { + let dummyStorageMethods; + + beforeEach(() => { + jest.clearAllMocks(); + + dummyStorageMethods = { + get: jest.fn(), + remove: jest.fn(), + set: jest.fn(), + }; + + // https://codewithhugo.com/jest-stub-mock-spy-set-clear/ + jest.spyOn(Storage.prototype, "validate"); + console.warn = jest.fn(); + console.error = jest.fn(); + }); + + it("should create not async Storage with normal Storage Methods (default config)", () => { + jest.spyOn(Storage.prototype, "validate").mockReturnValueOnce(true); + + const storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + }); + + expect(storage.key).toBe("customStorage"); + expect(storage.validate).toHaveBeenCalled(); + expect(storage.ready).toBe(true); + expect(storage.config).toStrictEqual({ + async: false, + prefix: "agile", + }); + expect(storage.methods).toStrictEqual(dummyStorageMethods); + }); + + it("should create not async Storage with normal Storage Methods (specific config)", () => { + jest.spyOn(Storage.prototype, "validate").mockReturnValueOnce(true); + + const storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + prefix: "testPrefix", + }); + + expect(storage.key).toBe("customStorage"); + expect(storage.validate).toHaveBeenCalled(); + expect(storage.ready).toBe(true); + expect(storage.config).toStrictEqual({ + async: false, + prefix: "testPrefix", + }); + expect(storage.methods).toStrictEqual(dummyStorageMethods); + }); + + it("should create async Storage with normal Storage Methods (config.async = true)", () => { + jest.spyOn(Storage.prototype, "validate").mockReturnValueOnce(true); + const storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + async: true, + }); + + expect(storage.key).toBe("customStorage"); + expect(storage.validate).toHaveBeenCalled(); + expect(storage.ready).toBe(true); + expect(storage.config).toStrictEqual({ + async: true, + prefix: "agile", + }); + expect(storage.methods).toStrictEqual(dummyStorageMethods); + }); + + it("should create async Storage with async Storage Methods (default config)", () => { + jest.spyOn(Storage.prototype, "validate").mockReturnValueOnce(true); + dummyStorageMethods.get = async () => jest.fn(); + + const storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + }); + + expect(storage.key).toBe("customStorage"); + expect(storage.validate).toHaveBeenCalled(); + expect(storage.ready).toBe(true); + expect(storage.config).toStrictEqual({ + async: true, + prefix: "agile", + }); + expect(storage.methods).toStrictEqual(dummyStorageMethods); + }); + + it("should create invalid Storage with normal Storage Methods if validate returns false (default config)", () => { + jest.spyOn(Storage.prototype, "validate").mockReturnValueOnce(false); + + const storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + }); + + expect(storage.key).toBe("customStorage"); + expect(storage.validate).toHaveBeenCalled(); + expect(storage.ready).toBe(false); + expect(storage.config).toStrictEqual({ + async: false, + prefix: "agile", + }); + expect(storage.methods).toStrictEqual(dummyStorageMethods); + }); + + describe("Storage Function Tests", () => { + let storage: Storage; + + beforeEach(() => { + storage = new Storage({ + key: "customStorage", + methods: dummyStorageMethods, + }); + }); + + describe("validate function tests", () => { + it("should return true if all methods are valid", () => { + const response = storage.validate(); + + expect(response).toBeTruthy(); + expect(console.error).not.toHaveBeenCalled(); + }); + + it("should return false if get method isn't valid", () => { + storage.methods.get = undefined; + + const response = storage.validate(); + + expect(response).toBeFalsy(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Your GET StorageMethod isn't valid!" + ); + }); + + it("should return false if set method isn't valid", () => { + storage.methods.set = undefined; + + const response = storage.validate(); + + expect(response).toBeFalsy(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Your SET StorageMethod isn't valid!" + ); + }); + + it("should return false if remove method isn't valid", () => { + storage.methods.remove = undefined; + + const response = storage.validate(); + + expect(response).toBeFalsy(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Your REMOVE StorageMethod isn't valid!" + ); + }); + }); + + describe("normalGet function tests", () => { + it("should call get method in storageMethods if Storage is ready", () => { + storage.ready = true; + storage.methods.get = jest.fn(() => "dummyResponse"); + + const response = storage.normalGet("myTestKey"); + + expect(response).toBe("dummyResponse"); + expect(storage.methods.get).toHaveBeenCalledWith( + storage.getStorageKey("myTestKey") + ); + }); + + it("should call get method in storageMethods and resolve json if Storage is ready", () => { + storage.ready = true; + storage.methods.get = jest.fn(() => [ + { + dummy: "json", + }, + ]); + + const response = storage.normalGet("myTestKey"); + + expect(response).toStrictEqual([{ dummy: "json" }]); + expect(storage.methods.get).toHaveBeenCalledWith( + storage.getStorageKey("myTestKey") + ); + }); + + it("shouldn't call get method in storageMethods if Storage isn't ready", () => { + storage.ready = false; + + const response = storage.normalGet("myTestKey"); + + expect(response).toBeUndefined(); + expect(storage.methods.get).not.toHaveBeenCalled(); + }); + + it("should call get method in storageMethods and print warning if get method is async", async () => { + storage.ready = true; + storage.methods.get = async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + return "dummyResponse"; + }; + + const response = storage.normalGet("myTestKey"); + expect(response).toBeInstanceOf(Promise); + // Couldn't figure out how to create an async mock function + // expect(storage.methods.get).toHaveBeenCalledWith( + // storage.getStorageKey("myTestKey") + // ); + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Be aware that 'normalGet' returns a Promise with a stringified Value if using it in an async Storage!" + ); + + return response.then((value) => { + expect(value).toBe("dummyResponse"); + }); + }); + }); + + describe("get function tests", () => { + it("should call and await get method in storageMethods if Storage is ready", async () => { + storage.ready = true; + storage.methods.get = async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + return "dummyResponse"; + }; + + const response = await storage.get("myTestKey"); + + expect(response).toBe("dummyResponse"); + // Couldn't figure out how to create an async mock function + // expect(storage.methods.get).toHaveBeenCalledWith( + // storage.getStorageKey("myTestKey") + // ); + }); + + it("should call and await get method in storageMethods and resolve json if Storage is ready", async () => { + storage.ready = true; + storage.methods.get = async () => { + await new Promise((resolve) => setTimeout(resolve, 100)); + return [ + { + dummy: "json", + }, + ]; + }; + + const response = await storage.get("myTestKey"); + + expect(response).toStrictEqual([{ dummy: "json" }]); + // Couldn't figure out how to create an async mock function + // expect(storage.methods.get).toHaveBeenCalledWith( + // storage.getStorageKey("myTestKey") + // ); + }); + + it("shouldn't call get method in storageMethods if Storage isn't ready", async () => { + storage.ready = false; + + const response = await storage.get("myTestKey"); + + expect(response).toBeUndefined(); + expect(storage.methods.get).not.toHaveBeenCalled(); + }); + + it("should call normalGet if get method isn't async", async () => { + storage.ready = true; + storage.normalGet = jest.fn(() => "dummyResponse" as any); + + const response = await storage.get("myTestKey"); + + expect(response).toBe("dummyResponse"); + expect(storage.normalGet).toHaveBeenCalledWith("myTestKey"); + }); + }); + + describe("set function tests", () => { + it("should call set method in storageMethods if Storage is ready", () => { + storage.ready = true; + + storage.set("myTestKey", "hello there"); + + expect(storage.methods.set).toHaveBeenCalledWith( + storage.getStorageKey("myTestKey"), + JSON.stringify("hello there") + ); + }); + + it("shouldn't call set method in storageMethods if Storage isn't ready", () => { + storage.ready = false; + + storage.set("myTestKey", "hello there"); + + expect(storage.methods.set).not.toHaveBeenCalled(); + }); + }); + + describe("remove function tests", () => { + it("should call remove method in storageMethods if Storage is ready", () => { + storage.ready = true; + + storage.remove("myTestKey"); + + expect(storage.methods.remove).toHaveBeenCalledWith( + storage.getStorageKey("myTestKey") + ); + }); + + it("shouldn't call remove method in storageMethods if Storage isn't ready", () => { + storage.ready = false; + + storage.remove("myTestKey"); + + expect(storage.methods.remove).not.toHaveBeenCalled(); + }); + }); + + describe("getStorageKey function tests", () => { + it("should add prefix to passed key if prefix is set", () => { + storage.config.prefix = "test"; + + expect(storage.getStorageKey("coolKey")).toBe("_test_coolKey"); + }); + + it("shouldn't add prefix to passed key if prefix isn't set", () => { + storage.config.prefix = undefined; + + expect(storage.getStorageKey("coolKey")).toBe("coolKey"); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/storages/storages.test.ts b/packages/core/tests/unit/storages/storages.test.ts new file mode 100644 index 00000000..9644d496 --- /dev/null +++ b/packages/core/tests/unit/storages/storages.test.ts @@ -0,0 +1,391 @@ +import { Storages, Agile, Storage, Persistent } from "../../../src"; + +describe("Storages Tests", () => { + let dummyAgile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + jest.spyOn(Storages.prototype, "instantiateLocalStorage"); + console.error = jest.fn(); + console.warn = jest.fn(); + }); + + it("should create Storages (default config)", () => { + const storages = new Storages(dummyAgile); + + expect(storages.defaultStorage).toBeUndefined(); + expect(storages.storages).toStrictEqual({}); + expect(storages.persistentInstances.size).toBe(0); + expect(storages.instantiateLocalStorage).not.toHaveBeenCalled(); + }); + + it("should create Storages and should get a warning (config.localStorage = true)", () => { + const storages = new Storages(dummyAgile, { localStorage: true }); + + expect(storages.defaultStorage).toBeUndefined(); + expect(storages.storages).toStrictEqual({}); + expect(storages.persistentInstances.size).toBe(0); + expect(storages.instantiateLocalStorage).toHaveBeenCalled(); + }); + + describe("Storages Function Tests", () => { + let storages: Storages; + let dummyStorage1: Storage; + let dummyStorage2: Storage; + let dummyStorage3: Storage; + let dummyStorageMethods; + + beforeEach(() => { + storages = new Storages(dummyAgile); + dummyAgile.storages = storages; + dummyStorageMethods = { + get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + }; + + dummyStorage1 = new Storage({ + key: "storage1", + methods: dummyStorageMethods, + }); + dummyStorage2 = new Storage({ + key: "storage2", + methods: dummyStorageMethods, + }); + dummyStorage3 = new Storage({ + key: "storage3", + methods: dummyStorageMethods, + }); + }); + + describe("instantiateLocalStorage function tests", () => { + beforeEach(() => { + global.localStorage = { + getItem: jest.fn(), + removeItem: jest.fn(), + setItem: jest.fn(), + } as any; + + storages.register = jest.fn(() => true); + }); + + it("should register localStorage if localStorage is available", () => { + jest.spyOn(Storages, "localStorageAvailable").mockReturnValueOnce(true); + + const response = storages.instantiateLocalStorage(); + + expect(response).toBeTruthy(); + expect(storages.register).toHaveBeenCalledWith(expect.any(Storage), { + default: true, + }); + }); + + it("shouldn't register localStorage if localStorage isn't available", () => { + jest + .spyOn(Storages, "localStorageAvailable") + .mockReturnValueOnce(false); + + const response = storages.instantiateLocalStorage(); + + expect(response).toBeFalsy(); + expect(storages.register).not.toHaveBeenCalled(); + }); + }); + + describe("register function tests", () => { + it("should register Storage and assign it as default Storage (default config)", () => { + const response = storages.register(dummyStorage1); + + expect(storages.storages).toHaveProperty("storage1"); + expect(storages.storages["storage1"]).toBe(dummyStorage1); + expect(storages.defaultStorage).toBe(dummyStorage1); + expect(response).toBeTruthy(); + }); + + it("should register Storage and assign it as default Storage with a warning (config.default = false)", () => { + const response = storages.register(dummyStorage1, { default: false }); + + expect(console.warn).toHaveBeenCalledWith( + "Agile Warn: Be aware that Agile has to assign the first added Storage as default Storage!" + ); + + expect(storages.storages).toHaveProperty("storage1"); + expect(storages.storages["storage1"]).toBe(dummyStorage1); + expect(storages.defaultStorage).toBe(dummyStorage1); + expect(response).toBeTruthy(); + }); + + it("should register second Storage and shouldn't assign it as default Storage (default config)", () => { + storages.register(dummyStorage1); + + const response = storages.register(dummyStorage2); + + expect(storages.storages).toHaveProperty("storage2"); + expect(storages.storages["storage2"]).toBe(dummyStorage2); + expect(storages.defaultStorage).toBe(dummyStorage1); + expect(response).toBeTruthy(); + }); + + it("should register second Storage and should assign it as default Storage (config.default = true)", () => { + storages.register(dummyStorage1); + + const response = storages.register(dummyStorage2, { default: true }); + + expect(storages.storages).toHaveProperty("storage2"); + expect(storages.storages["storage2"]).toBe(dummyStorage2); + expect(storages.defaultStorage).toBe(dummyStorage2); + expect(response).toBeTruthy(); + }); + + it("shouldn't register Storage with the same key twice", () => { + const dummyStorage = new Storage({ + key: "storage1", + methods: dummyStorageMethods, + }); + storages.register(dummyStorage); + + const response = storages.register(dummyStorage1); + + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Storage with the key/name 'storage1' already exists" + ); + + expect(storages.storages).toHaveProperty("storage1"); + expect(storages.storages["storage1"]).toBe(dummyStorage); + expect(response).toBeFalsy(); + }); + + it("should call updateValue method on all persistent Instances that have the newly registered StorageKey", () => { + const dummyPersistent1 = new Persistent(dummyAgile, { + storageKeys: ["storage1"], + }); + const dummyPersistent2 = new Persistent(dummyAgile, { + storageKeys: ["notExistingStorage"], + }); + jest.spyOn(dummyPersistent1, "persistValue"); + jest.spyOn(dummyPersistent2, "persistValue"); + + const response = storages.register(dummyStorage1); + + expect(dummyPersistent1.persistValue).toHaveBeenCalled(); + expect(dummyPersistent2.persistValue).not.toHaveBeenCalled(); + expect(response).toBeTruthy(); + }); + + it("should reassignStorageKeys, revalidate and initialLoad Persistents that have no defined defaultStorage", () => { + const dummyPersistent1 = new Persistent(dummyAgile); + dummyPersistent1.defaultStorageKey = undefined; + const dummyPersistent2 = new Persistent(dummyAgile); + dummyPersistent2.defaultStorageKey = "unknown"; + jest.spyOn(dummyPersistent1, "assignStorageKeys"); + jest + .spyOn(dummyPersistent1, "validatePersistent") + .mockReturnValue(true); + jest.spyOn(dummyPersistent1, "initialLoading"); + jest.spyOn(dummyPersistent2, "assignStorageKeys"); + jest.spyOn(dummyPersistent2, "validatePersistent"); + jest.spyOn(dummyPersistent2, "initialLoading"); + + const response = storages.register(dummyStorage1); + + expect(dummyPersistent1.assignStorageKeys).toHaveBeenCalled(); + expect(dummyPersistent1.validatePersistent).toHaveBeenCalled(); + expect(dummyPersistent1.initialLoading).toHaveBeenCalled(); + + expect(dummyPersistent2.assignStorageKeys).not.toHaveBeenCalled(); + expect(dummyPersistent2.validatePersistent).not.toHaveBeenCalled(); + expect(dummyPersistent2.initialLoading).not.toHaveBeenCalled(); + + expect(response).toBeTruthy(); + }); + }); + + describe("getStorage function tests", () => { + beforeEach(() => { + storages.register(dummyStorage1); + }); + + it("should get existing Storage", () => { + const response = storages.getStorage("storage1"); + + expect(response).toBe(dummyStorage1); + }); + + it("shouldn't get not existing Storage", () => { + const response = storages.getStorage("notExistingStorage"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Storage with the key/name 'notExistingStorage' doesn't exist" + ); + }); + + it("shouldn't get existing and not ready Storage", () => { + dummyStorage1.ready = false; + + const response = storages.getStorage("storage1"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Storage with the key/name 'storage1' isn't ready" + ); + }); + }); + + describe("get function tests", () => { + beforeEach(() => { + storages.register(dummyStorage1); + storages.register(dummyStorage2); + + dummyStorage1.get = jest.fn(() => "dummyStorage1Response" as any); + dummyStorage2.get = jest.fn(() => "dummyStorage2Response" as any); + }); + + it("should call get method in default Storage", async () => { + const response = await storages.get("value1"); + + expect(response).toBe("dummyStorage1Response"); + expect(dummyStorage1.get).toHaveBeenCalledWith("value1"); + expect(dummyStorage2.get).not.toHaveBeenCalled(); + }); + + it("should call get method in specific Storage", async () => { + const response = await storages.get("value1", "storage2"); + + expect(response).toBe("dummyStorage2Response"); + expect(dummyStorage1.get).not.toHaveBeenCalled(); + expect(dummyStorage2.get).toHaveBeenCalledWith("value1"); + }); + + it("should call get method in default Storage if specific Storage doesn't exist", async () => { + const response = await storages.get("value1", "notExistingStorage"); + + expect(response).toBe("dummyStorage1Response"); + expect(dummyStorage1.get).toHaveBeenCalledWith("value1"); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Storage with the key/name 'notExistingStorage' doesn't exist" + ); + }); + + it("should print error if no storage found", async () => { + const storages2 = new Storages(dummyAgile); + + const response = await storages2.get("value1"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No Storage found! Please provide at least one Storage." + ); + }); + }); + + describe("set function tests", () => { + beforeEach(() => { + storages.register(dummyStorage1); + storages.register(dummyStorage2); + storages.register(dummyStorage3); + + dummyStorage1.set = jest.fn(); + dummyStorage2.set = jest.fn(); + dummyStorage3.set = jest.fn(); + }); + + it("should call set method in default Storage", () => { + storages.set("value1", "testValue"); + + expect(dummyStorage1.set).toHaveBeenCalledWith("value1", "testValue"); + expect(dummyStorage2.set).not.toHaveBeenCalled(); + expect(dummyStorage3.set).not.toHaveBeenCalled(); + }); + + it("should call set method in specific Storages", () => { + storages.set("value1", "testValue", ["storage2", "storage3"]); + + expect(dummyStorage1.set).not.toHaveBeenCalled(); + expect(dummyStorage2.set).toHaveBeenCalledWith("value1", "testValue"); + expect(dummyStorage3.set).toHaveBeenCalledWith("value1", "testValue"); + }); + + it("should print error if no storage found", () => { + const storages2 = new Storages(dummyAgile); + + const response = storages2.set("value1", "testValue"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No Storage found! Please provide at least one Storage." + ); + }); + }); + + describe("remove function tests", () => { + beforeEach(() => { + storages.register(dummyStorage1); + storages.register(dummyStorage2); + storages.register(dummyStorage3); + + dummyStorage1.remove = jest.fn(); + dummyStorage2.remove = jest.fn(); + dummyStorage3.remove = jest.fn(); + }); + + it("should call remove method in default Storage", () => { + storages.remove("value1"); + + expect(dummyStorage1.remove).toHaveBeenCalledWith("value1"); + expect(dummyStorage2.remove).not.toHaveBeenCalled(); + expect(dummyStorage3.remove).not.toHaveBeenCalled(); + }); + + it("should call remove method in specific Storages", () => { + storages.remove("value1", ["storage2", "storage3"]); + + expect(dummyStorage1.remove).not.toHaveBeenCalled(); + expect(dummyStorage2.remove).toHaveBeenCalledWith("value1"); + expect(dummyStorage3.remove).toHaveBeenCalledWith("value1"); + }); + + it("should print error if no storage found", () => { + const storages2 = new Storages(dummyAgile); + + const response = storages2.remove("value1"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: No Storage found! Please provide at least one Storage." + ); + }); + }); + + describe("hasStorage function tests", () => { + it("should return true if Storages has registered Storages", () => { + storages.register(dummyStorage1); + + expect(storages.hasStorage()).toBeTruthy(); + }); + + it("should return false if Storages has no registered Storage", () => { + expect(storages.hasStorage()).toBeFalsy(); + }); + }); + + describe("localStorageAvailable function tests", () => { + it("should return true if a instance of local Storage exists", () => { + global.localStorage = { + getItem: jest.fn(), + removeItem: jest.fn(), + setItem: jest.fn(), + } as any; + + expect(Storages.localStorageAvailable()).toBeTruthy(); + }); + + it("should return false if no instance of localStorage exits", () => { + global.localStorage = undefined; + + expect(Storages.localStorageAvailable()).toBeFalsy(); + }); + }); + }); +}); diff --git a/packages/core/tests/unit/utils.test.ts b/packages/core/tests/unit/utils.test.ts new file mode 100644 index 00000000..fdff28da --- /dev/null +++ b/packages/core/tests/unit/utils.test.ts @@ -0,0 +1,569 @@ +import { + clone, + copy, + defineConfig, + equal, + flatMerge, + generateId, + includesArray, + isAsyncFunction, + isFunction, + isJsonString, + isValidObject, + isValidUrl, + normalizeArray, + notEqual, + globalBind, + getAgileInstance, + Agile, + State, + Event, + Observer, + Collection, +} from "../../src"; + +describe("Utils Tests", () => { + let dummyAgile: Agile; + + beforeEach(() => { + dummyAgile = new Agile({ localStorage: false }); + + // @ts-ignore | Reset globalThis + globalThis = {}; + + console.error = jest.fn(); + }); + + describe("copy function tests", () => { + it("should copy Array without any reference", () => { + const myArray = [1, 2, 3, 4, 5]; + const myCopiedArray = copy(myArray); + + expect(myCopiedArray).toStrictEqual([1, 2, 3, 4, 5]); + expect(myArray).toStrictEqual([1, 2, 3, 4, 5]); + + myCopiedArray.push(6); + + expect(myCopiedArray).toStrictEqual([1, 2, 3, 4, 5, 6]); + expect(myArray).toStrictEqual([1, 2, 3, 4, 5]); + }); + + it("should copy Object without any reference", () => { + const myObject = { id: 1, name: "jeff" }; + const myCopiedObject = copy(myObject); + + expect(myCopiedObject).toStrictEqual({ id: 1, name: "jeff" }); + expect(myObject).toStrictEqual({ id: 1, name: "jeff" }); + + myObject.name = "hans"; + + expect(myObject).toStrictEqual({ id: 1, name: "hans" }); + expect(myCopiedObject).toStrictEqual({ id: 1, name: "jeff" }); + }); + + it("should copy deep Object without any reference", () => { + const myObject = { + id: 1, + name: "jeff", + location: { country: "Germany", state: "Bayern" }, + }; + const myCopiedObject = copy(myObject); + + expect(myCopiedObject).toStrictEqual({ + id: 1, + name: "jeff", + location: { country: "Germany", state: "Bayern" }, + }); + expect(myObject).toStrictEqual({ + id: 1, + name: "jeff", + location: { country: "Germany", state: "Bayern" }, + }); + + myObject.name = "hans"; + myObject.location.state = "Sachsen"; + + expect(myObject).toStrictEqual({ + id: 1, + name: "hans", + location: { country: "Germany", state: "Sachsen" }, + }); + expect(myCopiedObject).toStrictEqual({ + id: 1, + name: "jeff", + location: { country: "Germany", state: "Bayern" }, + }); + }); + + it("should copy default Types", () => { + const myNumber = 5; + const myCopiedNumber = copy(myNumber); + const myString = "frank"; + const myCopiedString = copy(myString); + + expect(myCopiedNumber).toBe(5); + expect(myNumber).toBe(5); + expect(myCopiedString).toBe("frank"); + expect(myString).toBe("frank"); + }); + }); + + describe("isValidObject function tests", () => { + // Can't be Tested in not Web-Environment + // it("should return false if passing HTML Element", () => { + // expect(isValidObject(HTMLElement)).toBe(false); + // }); + + it("should return false if passed instance is invalid Object", () => { + expect(isValidObject(null)).toBe(false); + expect(isValidObject("Hello")).toBe(false); + expect(isValidObject([1, 2])).toBe(false); + expect(isValidObject(123)).toBe(false); + }); + + it("should return true if passed instance is valid Object", () => { + expect(isValidObject({ hello: "jeff" })).toBe(true); + expect(isValidObject({ hello: "jeff", deep: { hello: "franz" } })).toBe( + true + ); + }); + }); + + describe("includesArray function tests", () => { + it("should return false if Array1 doesn't include Array2", () => { + expect(includesArray([1, 2], [5, 6])).toBe(false); + }); + + it("should return false if Array1 does only include parts of Array2", () => { + expect(includesArray([1, 2], [2, 6])).toBe(false); + }); + + it("should return true if Array1 includes Array2", () => { + expect(includesArray([1, 4, 2, 3], [1, 2])).toBe(true); + }); + + it("should return true if Array1 is equal to Array2", () => { + expect(includesArray([1, 2], [1, 2])).toBe(true); + }); + }); + + describe("normalizeArray function tests", () => { + it("should normalize Array (default config)", () => { + expect(normalizeArray([1, 2, undefined, 3, "hi"])).toStrictEqual([ + 1, + 2, + undefined, + 3, + "hi", + ]); + }); + + it("should normalize single Item (default config)", () => { + expect(normalizeArray(1)).toStrictEqual([1]); + }); + + it("shouldn't normalize undefined (default config)", () => { + expect(normalizeArray(undefined)).toStrictEqual([]); + }); + + it("should normalize undefined (config.createUndefinedArray = true)", () => { + expect( + normalizeArray(undefined, { createUndefinedArray: true }) + ).toStrictEqual([undefined]); + }); + }); + + describe("getAgileInstance function tests", () => { + beforeEach(() => { + globalThis["__agile__"] = dummyAgile; + }); + + it("should get agileInstance from State", () => { + const dummyState = new State(dummyAgile, "dummyValue"); + + expect(getAgileInstance(dummyState)).toBe(dummyAgile); + }); + + it("should get agileInstance from Event", () => { + const dummyEvent = new Event(dummyAgile); + + expect(getAgileInstance(dummyEvent)).toBe(dummyAgile); + }); + + it("should get agileInstance from Collection", () => { + const dummyCollection = new Collection(dummyAgile); + + expect(getAgileInstance(dummyCollection)).toBe(dummyAgile); + }); + + it("should get agileInstance from Observer", () => { + const dummyObserver = new Observer(dummyAgile); + + expect(getAgileInstance(dummyObserver)).toBe(dummyAgile); + }); + + it("should get agileInstance from globalThis if passed instance holds no agileInstance", () => { + expect(getAgileInstance("weiredInstance")).toBe(dummyAgile); + }); + + it("should print error if something went wrong", () => { + // @ts-ignore | Destroy globalThis + globalThis = undefined; + + const response = getAgileInstance("weiredInstance"); + + expect(response).toBeUndefined(); + expect(console.error).toHaveBeenCalledWith( + "Agile Error: Failed to get Agile Instance from ", + "weiredInstance" + ); + }); + }); + + describe("isFunction function tests", () => { + it("should return true if passed instance is valid Function", () => { + expect(isFunction(() => {})).toBe(true); + }); + + it("should return false if passed instance is invalid Function", () => { + expect(isFunction("hello")).toBe(false); + expect(isFunction(1)).toBe(false); + expect(isFunction([1, 2, 3])).toBe(false); + expect(isFunction({ hello: "jeff" })).toBe(false); + }); + }); + + describe("isAsyncFunction function tests", () => { + it("should return true if passed instance is valid async Function", () => { + expect(isAsyncFunction(async () => {})).toBe(true); + expect(isAsyncFunction(async function () {})).toBe(true); + }); + + it("should return false if passed instance is invalid async Function", () => { + expect(isAsyncFunction("hello")).toBe(false); + expect(isAsyncFunction(1)).toBe(false); + expect(isAsyncFunction([1, 2, 3])).toBe(false); + expect(isAsyncFunction({ hello: "jeff" })).toBe(false); + expect(isAsyncFunction(() => {})).toBe(false); + expect(isAsyncFunction(function () {})).toBe(false); + }); + }); + + // Note: isValidUrl Function doesn't work to 100% yet!! + describe("isValidUrl function tests", () => { + it("should return true if passed instance is valid url", () => { + expect(isValidUrl("https://www.google.com/")).toBe(true); + expect(isValidUrl("www.google.com")).toBe(true); + expect(isValidUrl("google.com")).toBe(true); + // expect(isValidUrl("https://en.wikipedia.org/wiki/Procter_&_Gamble")).toBe( + // true + // ); + }); + + it("should return false if passed instance is invalid url", () => { + expect(isValidUrl("hello")).toBe(false); + expect(isValidUrl("https://sdfasd")).toBe(false); + expect(isValidUrl("https://")).toBe(false); + expect(isValidUrl("")).toBe(false); + // expect(isValidUrl("www.google")).toBe(false); + }); + }); + + describe("isJsonString function tests", () => { + it("should return true if passed instance is valid Json String", () => { + expect(isJsonString('{"name":"John", "age":31, "city":"New York"}')).toBe( + true + ); + }); + + it("should return false if passed instance is invalid Json String", () => { + expect(isJsonString("frank")).toBe(false); + expect(isJsonString('{name":"John", "age":31, "city":"New York"}')).toBe( + false + ); + expect(isJsonString(10)).toBe(false); + expect(isJsonString({ name: "John", age: 31 })).toBe(false); + }); + }); + + describe("defineConfig function tests", () => { + it("should merge defaults into config and overwrite undefined properties (default config)", () => { + const config = { + allowLogging: true, + loops: 10, + isHuman: undefined, + }; + expect( + defineConfig(config, { + allowLogging: false, + loops: 15, + isHuman: true, + isRobot: false, + name: "jeff", + }) + ).toStrictEqual({ + allowLogging: true, + loops: 10, + isHuman: true, + isRobot: false, + name: "jeff", + }); + }); + + it("should merge defaults into config and shouldn't overwrite undefined properties (overwriteUndefinedProperties = false)", () => { + const config = { + allowLogging: true, + loops: 10, + isHuman: undefined, + }; + expect( + defineConfig( + config, + { + allowLogging: false, + loops: 15, + isHuman: true, + isRobot: false, + name: "jeff", + }, + false + ) + ).toStrictEqual({ + allowLogging: true, + loops: 10, + isHuman: undefined, + isRobot: false, + name: "jeff", + }); + }); + }); + + describe("flatMerge function tests", () => { + it("should merge Changes Object into Source Object", () => { + const source = { + id: 123, + name: "jeff", + size: 189, + }; + expect( + flatMerge(source, { + name: "hans", + size: 177, + }) + ).toStrictEqual({ + id: 123, + name: "hans", + size: 177, + }); + }); + + it("shouldn't add new properties to Source Object", () => { + const source = { + id: 123, + name: "jeff", + size: 189, + }; + expect( + flatMerge(source, { + name: "hans", + size: 177, + location: "behind you", + }) + ).toStrictEqual({ + id: 123, + name: "hans", + size: 177, + }); + }); + + it("should add new properties to source Object (config.addNewProperties = true)", () => { + const source = { + id: 123, + name: "jeff", + size: 189, + }; + expect( + flatMerge( + source, + { + name: "hans", + size: 177, + location: "behind you", + }, + { addNewProperties: true } + ) + ).toStrictEqual({ + id: 123, + name: "hans", + size: 177, + location: "behind you", + }); + }); + + it("shouldn't deep merge Changes Object into Source Object", () => { + const source = { + id: 123, + name: "jeff", + address: { + place: "JeffsHome", + country: "Germany", + }, + }; + + expect( + flatMerge(source, { + place: "JeffsNewHome", + }) + ).toStrictEqual({ + id: 123, + name: "jeff", + address: { + place: "JeffsHome", + country: "Germany", + }, + }); + }); + }); + + describe("equal function tests", () => { + it("should return true if value1 and value2 are equal", () => { + expect(equal({ id: 123, name: "jeff" }, { id: 123, name: "jeff" })).toBe( + true + ); + expect(equal([1, 2, 3], [1, 2, 3])).toBe(true); + expect(equal(12, 12)).toBe(true); + expect(equal("hi", "hi")).toBe(true); + }); + + it("should return false if value1 and value2 aren't equal", () => { + expect(equal({ id: 123, name: "jeff" }, { id: 123, name: "hans" })).toBe( + false + ); + expect(equal([1, 2], [3, 5])).toBe(false); + expect(equal(12, 13)).toBe(false); + expect(equal("hi", "bye")).toBe(false); + }); + }); + + describe("notEqual function tests", () => { + it("should return false if value1 and value2 are equal", () => { + expect( + notEqual({ id: 123, name: "jeff" }, { id: 123, name: "jeff" }) + ).toBe(false); + expect(notEqual([1, 2, 3], [1, 2, 3])).toBe(false); + expect(notEqual(12, 12)).toBe(false); + expect(equal("hi", "bye")).toBe(false); + }); + + it("should return true if value1 and value2 aren't equal", () => { + expect( + notEqual({ id: 123, name: "jeff" }, { id: 123, name: "hans" }) + ).toBe(true); + expect(notEqual([1, 2], [3, 5])).toBe(true); + expect(notEqual(12, 13)).toBe(true); + expect(notEqual("hi", "bye")).toBe(true); + }); + }); + + describe("generateId function tests", () => { + it("should returned generated Id that matches regex", () => { + expect(generateId()).toMatch(/^[a-zA-Z0-9]*$/); + }); + + it("should returned generated Id with correct length (length = x)", () => { + expect(generateId(10)).toMatch(/^[a-zA-Z0-9]*$/); + expect(generateId(10).length).toEqual(10); + expect(generateId(5).length).toEqual(5); + expect(generateId(-10).length).toEqual(0); + }); + }); + + describe("clone function tests", () => { + it("should clone Object/Class without any reference", () => { + class DummyClass { + constructor( + public id: number, + public name: string, + public location: { country: string; state: string } + ) {} + } + const dummyClass = new DummyClass(10, "jeff", { + country: "USA", + state: "California", + }); + const clonedDummyClass = clone(dummyClass); + + expect(dummyClass).toBeInstanceOf(DummyClass); + expect(clonedDummyClass).toBeInstanceOf(DummyClass); + expect(dummyClass.name).toBe("jeff"); + expect(dummyClass.id).toBe(10); + expect(dummyClass.location).toStrictEqual({ + country: "USA", + state: "California", + }); + expect(clonedDummyClass.name).toBe("jeff"); + expect(clonedDummyClass.id).toBe(10); + expect(clonedDummyClass.location).toStrictEqual({ + country: "USA", + state: "California", + }); + + dummyClass.name = "frank"; + dummyClass.location.state = "Florida"; + + expect(dummyClass.name).toBe("frank"); + expect(dummyClass.id).toBe(10); + expect(dummyClass.location).toStrictEqual({ + country: "USA", + state: "Florida", + }); + expect(clonedDummyClass.name).toBe("jeff"); + expect(clonedDummyClass.id).toBe(10); + expect(clonedDummyClass.location).toStrictEqual({ + country: "USA", + state: "California", + }); + }); + }); + + describe("globalBind function tests", () => { + const dummyKey = "myDummyKey"; + + beforeEach(() => { + globalThis[dummyKey] = undefined; + }); + + it("should bind instance at key globally (default config)", () => { + globalBind(dummyKey, "dummyInstance"); + + expect(globalThis[dummyKey]).toBe("dummyInstance"); + }); + + it("shouldn't overwrite already existing instance at key (default config)", () => { + globalBind(dummyKey, "I am first!"); + + globalBind(dummyKey, "dummyInstance"); + + expect(globalThis[dummyKey]).toBe("I am first!"); + }); + + it("should overwrite already existing instance at key (overwrite = true)", () => { + globalBind(dummyKey, "I am first!"); + + globalBind(dummyKey, "dummyInstance", true); + + expect(globalThis[dummyKey]).toBe("dummyInstance"); + }); + + it("should print error if something went wrong during the bind process", () => { + // @ts-ignore | Destroy globalThis + globalThis = undefined; + + globalBind(dummyKey, "dummyInstance"); + + expect(console.error).toHaveBeenCalledWith( + `Agile Error: Failed to create global Instance called '${dummyKey}'` + ); + }); + }); +}); diff --git a/packages/multieditor/LICENCE b/packages/multieditor/LICENCE new file mode 100644 index 00000000..b93156fb --- /dev/null +++ b/packages/multieditor/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present bennodev19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/multieditor/jest.config.js b/packages/multieditor/jest.config.js new file mode 100644 index 00000000..0161f78b --- /dev/null +++ b/packages/multieditor/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require("../../jest.config.base"); + +module.exports = { + ...baseConfig, + rootDir: "../..", + name: "Multi Editor", +}; diff --git a/packages/multieditor/src/item.ts b/packages/multieditor/src/item.ts index f80f8b85..8ec86a6d 100644 --- a/packages/multieditor/src/item.ts +++ b/packages/multieditor/src/item.ts @@ -1,4 +1,8 @@ -import { defineConfig, SetConfigInterface, State } from "@agile-ts/core"; +import { + defineConfig, + State, + StateRuntimeJobConfigInterface, +} from "@agile-ts/core"; import { MultiEditor, Validator, Status, ItemKey } from "./internal"; export class Item extends State { @@ -24,7 +28,9 @@ export class Item extends State { key: ItemKey, config: ItemConfigInterface = {} ) { - super(editor.agileInstance(), data, key); + super(editor.agileInstance(), data, { + key: key, + }); config = defineConfig(config, { canBeEdited: true, }); @@ -70,7 +76,7 @@ export class Item extends State { * Resets State to its initial Value * @param config - Config */ - public reset(config: SetConfigInterface = {}): this { + public reset(config: StateRuntimeJobConfigInterface = {}): this { super.reset(config); this.status.display = false; return this; diff --git a/packages/multieditor/src/status/index.ts b/packages/multieditor/src/status/index.ts index 6c4f79d3..a0955390 100644 --- a/packages/multieditor/src/status/index.ts +++ b/packages/multieditor/src/status/index.ts @@ -1,4 +1,4 @@ -import {Agile, copy, JobConfigInterface} from "@agile-ts/core"; +import { Agile, copy, RuntimeJobConfigInterface } from "@agile-ts/core"; import { Item, StatusObserver } from "../internal"; export class Status { @@ -67,7 +67,7 @@ export class Status { * Assign last set Status Value to the current Status Value * @param config - Config */ - public assign(config: JobConfigInterface = {}) { + public assign(config: RuntimeJobConfigInterface = {}) { this.observer.assign(config); } diff --git a/packages/multieditor/src/status/status.observer.ts b/packages/multieditor/src/status/status.observer.ts index 43cbe85d..cb7aaeb9 100644 --- a/packages/multieditor/src/status/status.observer.ts +++ b/packages/multieditor/src/status/status.observer.ts @@ -4,10 +4,11 @@ import { copy, defineConfig, equal, - Job, - JobConfigInterface, + IngestConfigInterface, Observer, ObserverKey, + RuntimeJob, + RuntimeJobConfigInterface, } from "@agile-ts/core"; export class StatusObserver extends Observer { @@ -19,16 +20,18 @@ export class StatusObserver extends Observer { * Status Observer - Handles Status changes, dependencies (-> Interface to Runtime) * @param agileInstance - An instance of Agile * @param status - Status - * @param deps - Initial Dependencies of Status Observer - * @param key - Key/Name of State Observer + * @param config - Config */ constructor( agileInstance: Agile, status: Status, - deps?: Array, - key?: ObserverKey + config: StatusObserverConfigInterface = {} ) { - super(agileInstance, deps, key, status._value); + super(agileInstance, { + key: config.key, + deps: config.deps, + value: status._value, + }); this.status = () => status; this.nextValue = copy(status._value); } @@ -41,23 +44,31 @@ export class StatusObserver extends Observer { * Assigns next Status Value to current Status Value * @param config - Config */ - public assign(config: JobConfigInterface = {}): void { + public assign(config: StatusIngestConfigInterface = {}): void { config = defineConfig(config, { perform: true, background: false, sideEffects: true, force: false, - storage: true, }); // Set Next Status Value this.nextValue = copy(this.status().nextValue); // Check if Status changed - if (equal(this.status()._value, this.nextValue) && !config.force) - return; + if (equal(this.status()._value, this.nextValue) && !config.force) return; + + // Create Job + const job = new RuntimeJob(this, { + sideEffects: config.sideEffects, + force: config.force, + background: config.background, + key: this._key, + }); - this.agileInstance().runtime.ingest(this, config); + this.agileInstance().runtime.ingest(job, { + perform: config.perform, + }); } //========================================================================================================= @@ -68,7 +79,7 @@ export class StatusObserver extends Observer { * Performs Job from Runtime * @param job - Job that gets performed */ - public perform(job: Job) { + public perform(job: RuntimeJob) { const status = job.observer.status(); // Set new State Value @@ -79,3 +90,16 @@ export class StatusObserver extends Observer { this.value = copy(this.nextValue); } } + +/** + * @param deps - Initial Dependencies of Status Observer + * @param key - Key/Name of Status Observer + */ +export interface StatusObserverConfigInterface { + deps?: Array; + key?: ObserverKey; +} + +export interface StatusIngestConfigInterface + extends RuntimeJobConfigInterface, + IngestConfigInterface {} diff --git a/packages/multieditor/tests/default.spec.ts b/packages/multieditor/tests/old/default.spec.ts similarity index 100% rename from packages/multieditor/tests/default.spec.ts rename to packages/multieditor/tests/old/default.spec.ts diff --git a/packages/react/LICENCE b/packages/react/LICENCE new file mode 100644 index 00000000..b93156fb --- /dev/null +++ b/packages/react/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present bennodev19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/react/jest.config.js b/packages/react/jest.config.js new file mode 100644 index 00000000..cadc764f --- /dev/null +++ b/packages/react/jest.config.js @@ -0,0 +1,7 @@ +const baseConfig = require("../../jest.config.base"); + +module.exports = { + ...baseConfig, + rootDir: "../..", + name: "React", +}; diff --git a/packages/react/src/hooks/AgileHOC.ts b/packages/react/src/hooks/AgileHOC.ts index 4dfb95e3..2b684180 100644 --- a/packages/react/src/hooks/AgileHOC.ts +++ b/packages/react/src/hooks/AgileHOC.ts @@ -55,21 +55,26 @@ export function AgileHOC( return ReactComponent; } - return class extends React.Component { - public componentSubscriptionContainer: ComponentSubscriptionContainer | null = null; // Will be set and used in sub.ts + return class extends ReactComponent { + public agileInstance: () => Agile; + public componentSubscriptionContainer: ComponentSubscriptionContainer | null = null; // Will be set and used in sub.ts public updatedProps = this.props; constructor(props: any) { super(props); + this.agileInstance = (() => agileInstance) as any; // Create HOC based Subscription with Array (Rerenders will here be caused via force Update) if (depsArray) - agileInstance?.subController.subscribeWithSubsArray(this, depsArray); + this.agileInstance().subController.subscribeWithSubsArray( + this, + depsArray + ); // Create HOC based Subscription with Object if (depsObject) { - const response = agileInstance?.subController.subscribeWithSubsObject( + const response = this.agileInstance().subController.subscribeWithSubsObject( this, depsObject ); @@ -84,12 +89,12 @@ export function AgileHOC( } componentDidMount() { - if (agileInstance?.config.waitForMount) - agileInstance?.subController.mount(this); + if (this.agileInstance().config.waitForMount) + this.agileInstance().subController.mount(this); } componentWillUnmount() { - agileInstance?.subController.unsubscribe(this); + this.agileInstance().subController.unsubscribe(this); } render() { @@ -97,3 +102,13 @@ export function AgileHOC( } }; } + +// Just for having a type save base in react.integration +export class AgileReactComponent extends React.Component { + public componentSubscriptionContainer: ComponentSubscriptionContainer | null = null; // Will be set and used in sub.ts + public updatedProps = this.props; + + constructor(props: any) { + super(props); + } +} diff --git a/packages/react/src/react.integration.ts b/packages/react/src/react.integration.ts index 39334af3..0cb7f828 100644 --- a/packages/react/src/react.integration.ts +++ b/packages/react/src/react.integration.ts @@ -1,14 +1,15 @@ import { Agile, Integration } from "@agile-ts/core"; +import { AgileReactComponent } from "./hooks/AgileHOC"; import React from "react"; -const reactIntegration = new Integration({ - name: "react", +const reactIntegration = new Integration({ + key: "react", frameworkInstance: React, bind(agileInstance: Agile) { // Nothing to bind ;D - return true; + return Promise.resolve(true); }, - updateMethod(componentInstance: any, updatedData: Object) { + updateMethod(componentInstance, updatedData: Object) { // UpdatedData will be empty if the AgileHOC doesn't get an object as deps if (Object.keys(updatedData).length !== 0) { diff --git a/packages/react/tests/useAgile.spec.ts b/packages/react/tests/old/useAgile.spec.ts similarity index 100% rename from packages/react/tests/useAgile.spec.ts rename to packages/react/tests/old/useAgile.spec.ts